package runlive_test

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

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

// TestIntrospectionFansOut is the regression net for SR1-SR5: when the
// user prompt classifies as introspection, master.fanOut must dispatch
// a Delegate envelope to EVERY child role declared in master's
// delegates_to (not pick one).
//
// We assert on the events table — at minimum we expect:
//   - one master.fan_out.dispatched per target
//   - each child receives the delegate (message.delivered + agent.handle_one.started)
//
// Self-report tool emission depends on the LLM behaviour; in scripted
// mode the production behaviors don't currently call self_report (the
// scripted runtime predates the tool). That's left to the live-CLI E2E
// run for verification. This test guards the routing machinery so a
// regression in master.fanOut surfaces immediately.
func TestIntrospectionFansOut(t *testing.T) {
	tmp := t.TempDir()
	st, err := store.Open(filepath.Join(tmp, "harness.db"))
	if err != nil {
		t.Fatal(err)
	}
	defer st.Close()
	bus := event.NewBus(st)
	q := transport.New(st, bus)
	orch := orchestrator.New(st, bus)

	sub, cancel := bus.Subscribe(2048)
	defer cancel()
	var events []event.Event
	done := make(chan struct{})
	go func() {
		defer close(done)
		for ev := range sub {
			events = append(events, ev)
		}
	}()

	root := repoRoot(t)
	// hierarchical-v1 has master → fe-lead → fe-worker AND master →
	// be-lead → be-worker. fanOut should hit BOTH leads.
	orgPath := filepath.Join(root, ".td", "orgs", "hierarchical-v1.yaml")

	ctx, ctxCancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer ctxCancel()

	_, err = runlive.Run(ctx, st, bus, q, orch, runlive.Config{
		OrgPath:      orgPath,
		Prompt:       "Let every subworker identify and self-report itself",
		RuntimeMode:  "scripted",
		MaxWall:      10 * time.Second,
		StallTimeout: 10 * time.Second,
		ProjectRoot:  filepath.Join(tmp, "project"),
	})
	if err != nil {
		t.Fatalf("runlive.Run: %v", err)
	}
	cancel()
	<-done

	// Count fan_out.dispatched events — must equal the number of children
	// master declares (fe-lead, be-lead in hierarchical-v1).
	dispatched := map[string]int{}
	for _, ev := range events {
		if ev.Kind != "master.fan_out.dispatched" {
			continue
		}
		role, _ := ev.Payload["target_role"].(string)
		dispatched[role]++
	}
	if dispatched["fe-lead"] != 1 {
		t.Errorf("master should have dispatched introspection to fe-lead once; got %d", dispatched["fe-lead"])
	}
	if dispatched["be-lead"] != 1 {
		t.Errorf("master should have dispatched introspection to be-lead once; got %d", dispatched["be-lead"])
	}
}
