package tunergenius

import (
	"bufio"
	"net"
	"os"
	"strings"
	"testing"
	"time"
)

// captureLog redirects the stream applog.Printf always writes to, and returns a
// stop function yielding what was logged. Reading only after stop keeps the
// collector goroutine and the test off the same slice.
func captureLog() func() []string {
	orig := os.Stderr
	r, w, _ := os.Pipe()
	os.Stderr = w
	var lines []string
	done := make(chan struct{})
	go func() {
		sc := bufio.NewScanner(r)
		for sc.Scan() {
			lines = append(lines, sc.Text())
		}
		close(done)
	}()
	return func() []string {
		os.Stderr = orig
		w.Close()
		<-done
		r.Close()
		return lines
	}
}

// A tuner that answers, then vanishes mid-session. A drop used to leave no trace
// at all, so "did the tuner disconnect, or is the meter just reading a gap
// between syllables?" was unanswerable from the log. It must now log — and log
// ONCE, not on every 400 ms retry.
func TestLinkDownLoggedOnceNotPerRetry(t *testing.T) {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	port := ln.Addr().(*net.TCPAddr).Port

	die := make(chan struct{})
	go func() {
		conn, err := ln.Accept()
		if err != nil {
			return
		}
		conn.Write([]byte("V1.2.11\n"))
		buf := make([]byte, 256)
		for {
			n, err := conn.Read(buf)
			if err != nil {
				return
			}
			id := strings.TrimPrefix(strings.SplitN(string(buf[:n]), "|", 2)[0], "C")
			conn.Write([]byte("R" + id + "|0|status state=1 fwd=60.7 swr=-25 active=1\n"))
			select {
			case <-die:
				conn.Close()
				ln.Close()
				return
			default:
			}
		}
	}()

	stop := captureLog()
	c := New("127.0.0.1", port, "")
	if err := c.Start(); err != nil {
		stop()
		t.Fatal(err)
	}

	time.Sleep(1200 * time.Millisecond)
	connected := c.GetStatus().Connected
	close(die)
	time.Sleep(2 * time.Second) // ~5 poll cycles with the tuner gone
	c.Stop()
	lines := stop()

	if !connected {
		t.Fatal("the client never reached the fake tuner — the rest of the test is meaningless")
	}
	down := 0
	for _, l := range lines {
		if strings.Contains(l, "link DOWN") {
			down++
		}
	}
	t.Logf("logged:\n%s", strings.Join(lines, "\n"))
	if down != 1 {
		t.Errorf("got %d 'link DOWN' lines, want exactly 1 — the poll retries many times in a 3 s outage and must not repeat itself", down)
	}
}
