package localapi

import (
	"bytes"
	"encoding/json"
	"io"
	"log/slog"
	"strings"
	"testing"

	applog "gitea.dooplex.hu/admin/felhom-agent/internal/log"
)

// newDebugLogServer builds a minimal server with the debug ring wired and one line
// of each level captured (emit level info — the capture-at-info posture).
func newDebugLogServer(t *testing.T) (*Server, *applog.Ring) {
	t.Helper()
	logger, ring := applog.NewWithWriter(&bytes.Buffer{}, "info", 50)
	logger.Debug("netverify: flow detail", "step", "probe")
	logger.Info("netmount: ensured network mount", "name", "nas")
	srv, err := NewServer(Options{
		ListenAddr: "127.0.0.1:0",
		Guests:     &fakeGuests{},
		Backups:    &fakeBackups{},
		Store:      &fakeStore{},
		Storage:    fakeStorage{},
		Tokens:     staticTokens{"A": 8200},
		LogRing:    ring,
		Logger:     slog.New(slog.NewTextHandler(io.Discard, nil)),
	})
	if err != nil {
		t.Fatalf("new server: %v", err)
	}
	return srv, ring
}

// GET /debug/logs serves the ring's entries — INCLUDING the DEBUG line captured at
// emit level info (the endpoint exists so the controller's agent tab can show flow
// detail without a config flip). Companion red-proof: unwire LogRing → 503.
func TestDebugLogs_ServesRingEntriesIncludingDebug(t *testing.T) {
	srv, _ := newDebugLogServer(t)
	w := do(t, srv.Handler(), "GET", "/debug/logs", "A", "")
	if w.Code != 200 {
		t.Fatalf("status = %d body=%s", w.Code, w.Body.String())
	}
	var resp struct {
		OK   bool `json:"ok"`
		Data struct {
			Entries []applog.Entry `json:"entries"`
			Total   int            `json:"total"`
		} `json:"data"`
	}
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if !resp.OK || resp.Data.Total != 2 || len(resp.Data.Entries) != 2 {
		t.Fatalf("resp = %+v, want ok with 2 entries", resp)
	}
	if resp.Data.Entries[0].Level != "DEBUG" || !strings.Contains(resp.Data.Entries[0].Message, "flow detail") {
		t.Errorf("entry 0 = %+v, want the captured DEBUG line", resp.Data.Entries[0])
	}
}

// ?raw=1 serves plain text lines (the controller viewer's raw variant).
func TestDebugLogs_RawVariant(t *testing.T) {
	srv, _ := newDebugLogServer(t)
	w := do(t, srv.Handler(), "GET", "/debug/logs?raw=1", "A", "")
	if w.Code != 200 || !strings.HasPrefix(w.Header().Get("Content-Type"), "text/plain") {
		t.Fatalf("status=%d content-type=%q", w.Code, w.Header().Get("Content-Type"))
	}
	body := w.Body.String()
	if !strings.Contains(body, "[DEBUG]") || !strings.Contains(body, "ensured network mount") {
		t.Errorf("raw body missing lines:\n%s", body)
	}
}

// The route is auth-gated like every sibling (no token → 401), and reports "not
// configured" (503) when the ring is not wired — never a panic, never an empty 200.
func TestDebugLogs_AuthAndUnconfigured(t *testing.T) {
	srv, _ := newDebugLogServer(t)
	if w := do(t, srv.Handler(), "GET", "/debug/logs", "", ""); w.Code != 401 {
		t.Errorf("unauthed status = %d, want 401", w.Code)
	}
	bare, err := NewServer(Options{
		ListenAddr: "127.0.0.1:0",
		Guests:     &fakeGuests{},
		Backups:    &fakeBackups{},
		Store:      &fakeStore{},
		Storage:    fakeStorage{},
		Tokens:     staticTokens{"A": 8200},
		Logger:     slog.New(slog.NewTextHandler(io.Discard, nil)),
	})
	if err != nil {
		t.Fatalf("new server: %v", err)
	}
	if w := do(t, bare.Handler(), "GET", "/debug/logs", "A", ""); w.Code != 503 {
		t.Errorf("unconfigured status = %d, want 503", w.Code)
	}
}

// The request middleware logs method/path/status/duration at DEBUG into the wired
// logger — proven through a ring-backed server logger (the line must land in the ring).
func TestRequestLogging_DebugLineInRing(t *testing.T) {
	logger, ring := applog.NewWithWriter(&bytes.Buffer{}, "info", 50)
	srv, err := NewServer(Options{
		ListenAddr: "127.0.0.1:0",
		Guests:     &fakeGuests{},
		Backups:    &fakeBackups{},
		Store:      &fakeStore{},
		Storage:    fakeStorage{},
		Tokens:     staticTokens{"A": 8200},
		LogRing:    ring,
		Logger:     logger,
	})
	if err != nil {
		t.Fatalf("new server: %v", err)
	}
	do(t, srv.Handler(), "GET", "/storage", "A", "")
	entries, _ := ring.Entries(0)
	found := false
	for _, e := range entries {
		if e.Level == "DEBUG" && strings.Contains(e.Message, "local-api: request") &&
			strings.Contains(e.Message, "path=/storage") {
			found = true
		}
	}
	if !found {
		t.Errorf("no request DEBUG line in the ring; entries=%+v", entries)
	}
}
