package event

import (
	"context"
	"path/filepath"
	"testing"
	"time"

	"github.com/flothus/tmux-xterm-research/server-go/internal/harness/store"
)

func newBus(t *testing.T) (*Bus, *store.Store) {
	t.Helper()
	tmp := t.TempDir()
	st, err := store.Open(filepath.Join(tmp, "harness.db"))
	if err != nil {
		t.Fatalf("open store: %v", err)
	}
	t.Cleanup(func() { st.Close() })
	return NewBus(st), st
}

func TestEmitPersistsAndFansOut(t *testing.T) {
	bus, st := newBus(t)
	sub, cancel := bus.Subscribe(8)
	defer cancel()

	id, err := bus.Emit(context.Background(), Event{
		Kind:    KindTaskCreated,
		RunID:   "run-1",
		TaskID:  "task-1",
		Payload: map[string]any{"title": "do thing"},
	})
	if err != nil {
		t.Fatalf("Emit: %v", err)
	}
	if id <= 0 {
		t.Fatalf("Emit id = %d", id)
	}

	// Subscriber sees it.
	select {
	case ev := <-sub:
		if ev.Kind != KindTaskCreated {
			t.Errorf("subscriber kind = %s, want %s", ev.Kind, KindTaskCreated)
		}
		if ev.Classification != "task" {
			t.Errorf("classification = %q, want task", ev.Classification)
		}
	case <-time.After(time.Second):
		t.Fatal("subscriber never received event")
	}

	// Persisted row.
	var kind, class string
	err = st.DB().QueryRow(`SELECT kind, classification FROM events WHERE id=?`, id).Scan(&kind, &class)
	if err != nil {
		t.Fatalf("read row: %v", err)
	}
	if kind != string(KindTaskCreated) || class != "task" {
		t.Errorf("row kind/class = %s/%s", kind, class)
	}
}

func TestRegisterAndClassify(t *testing.T) {
	bus, _ := newBus(t)
	custom := Kind("custom.something")
	if got := bus.Classify(custom); got != ClassUnclassified {
		t.Errorf("before register: classification = %q, want unclassified", got)
	}
	bus.Register(custom, "custom-class")
	if got := bus.Classify(custom); got != "custom-class" {
		t.Errorf("after register: classification = %q", got)
	}
}

func TestSubscribeDropsOnSlow(t *testing.T) {
	bus, _ := newBus(t)
	sub, cancel := bus.Subscribe(1)
	defer cancel()

	// Fill the buffer + one more (drop expected).
	for i := 0; i < 5; i++ {
		_, err := bus.Emit(context.Background(), Event{Kind: KindAgentHeartbeat})
		if err != nil {
			t.Fatal(err)
		}
	}
	// We expect to consume at most buffer-size events without blocking.
	got := 0
	for {
		select {
		case <-sub:
			got++
		default:
			goto done
		}
	}
done:
	if got > 1 {
		// implementation queues at most buf events; rest are dropped silently
		t.Errorf("got %d events buffered, want <=1", got)
	}
	// V104: drops must be observable via SubscriberDropCount. We sent
	// 5 events with a buf=1 subscriber, so ≥3 should have been dropped.
	drops := bus.SubscriberDropCount()
	if drops < 3 {
		t.Errorf("SubscriberDropCount = %d, want >=3 (we sent 5 events to a buf=1 channel)", drops)
	}
}
