package handler

import (
	"context"
	"log/slog"
	"strings"
	"testing"
	"time"
)

func TestLogRingChronological(t *testing.T) {
	r := NewLogRing(3)

	// Push 5 entries; the oldest 2 should be evicted.
	for i := 0; i < 5; i++ {
		r.push(LogEntry{
			Time:    time.Unix(int64(i), 0).UTC(),
			Level:   "INFO",
			Message: "msg",
			Attrs:   map[string]any{"i": i},
		})
	}

	snap := r.Snapshot()
	if len(snap) != 3 {
		t.Fatalf("len(snap) = %d, want 3", len(snap))
	}
	for i, e := range snap {
		wantI := i + 2 // 2,3,4
		if got := e.Attrs["i"]; got != wantI {
			t.Errorf("snap[%d].Attrs.i = %v, want %d", i, got, wantI)
		}
	}
}

func TestLogRingEmpty(t *testing.T) {
	r := NewLogRing(5)
	if got := r.Snapshot(); len(got) != 0 {
		t.Errorf("empty ring snapshot len = %d, want 0", len(got))
	}
}

func TestLogRingPartialFill(t *testing.T) {
	r := NewLogRing(10)
	for i := 0; i < 3; i++ {
		r.push(LogEntry{Message: "x", Attrs: map[string]any{"i": i}})
	}
	if got := r.Snapshot(); len(got) != 3 {
		t.Errorf("partial-fill snapshot len = %d, want 3", len(got))
	}
}

func TestRingHandlerCapturesRecord(t *testing.T) {
	ring := NewLogRing(10)
	h := NewRingHandler(ring, slog.LevelInfo)
	logger := slog.New(h)

	logger.Info("hello", "user", "alice", "n", 7)

	snap := ring.Snapshot()
	if len(snap) != 1 {
		t.Fatalf("len(snap) = %d, want 1", len(snap))
	}
	e := snap[0]
	if e.Message != "hello" {
		t.Errorf("message = %q, want %q", e.Message, "hello")
	}
	if e.Level != slog.LevelInfo.String() {
		t.Errorf("level = %q, want %q", e.Level, slog.LevelInfo.String())
	}
	if e.Attrs["user"] != "alice" {
		t.Errorf("attrs.user = %v, want alice", e.Attrs["user"])
	}
	if e.Attrs["n"] != int64(7) {
		t.Errorf("attrs.n = %v (%T), want int64(7)", e.Attrs["n"], e.Attrs["n"])
	}
}

func TestRingHandlerLevelFilter(t *testing.T) {
	ring := NewLogRing(10)
	h := NewRingHandler(ring, slog.LevelWarn)
	logger := slog.New(h)

	logger.Debug("d")
	logger.Info("i")
	logger.Warn("w")
	logger.Error("e")

	snap := ring.Snapshot()
	if len(snap) != 2 {
		t.Fatalf("len(snap) = %d, want 2 (warn+error)", len(snap))
	}
	if snap[0].Message != "w" || snap[1].Message != "e" {
		t.Errorf("messages = [%q %q], want [w e]", snap[0].Message, snap[1].Message)
	}
}

func TestMultiHandlerFansOut(t *testing.T) {
	ring := NewLogRing(10)
	rh := NewRingHandler(ring, slog.LevelDebug)

	var buf strings.Builder
	th := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})

	multi := NewMultiHandler(th, rh)
	logger := slog.New(multi)

	logger.Info("teed", "a", 1)

	if !strings.Contains(buf.String(), "teed") {
		t.Errorf("text handler did not receive: %q", buf.String())
	}
	snap := ring.Snapshot()
	if len(snap) != 1 || snap[0].Message != "teed" {
		t.Errorf("ring did not receive: %+v", snap)
	}
}

func TestMultiHandlerEnabled(t *testing.T) {
	ring := NewLogRing(10)
	rhInfo := NewRingHandler(ring, slog.LevelInfo)
	rhError := NewRingHandler(ring, slog.LevelError)

	multi := NewMultiHandler(rhInfo, rhError)

	// Multi.Enabled should be true if ANY child is enabled.
	if !multi.Enabled(context.Background(), slog.LevelInfo) {
		t.Error("Enabled(Info) = false; expected true (rhInfo accepts it)")
	}
	if multi.Enabled(context.Background(), slog.LevelDebug) {
		t.Error("Enabled(Debug) = true; expected false (no child accepts it)")
	}
}
