package runtime

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"testing"

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

// TestRenderSystemPromptHasTeamFromStaticPeers is the root-cause
// regression test for the LLM-master rebuild. Symptom:
// {{peers}} in master.md expanded to an empty string because the live
// agents-table query in Introspect() returned no siblings at
// master-spawn time (master is spawned first in topo order). Master's
// system prompt literally said "Your team: ****" and the LLM had no
// targets to delegate to.
//
// Fix: orgrunner sets Agent.StaticPeers from the org definition before
// rendering. RenderSystemPrompt prefers StaticPeers when populated.
// This test verifies the fix without spinning up a full run.
func TestRenderSystemPromptHasTeamFromStaticPeers(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)
	// Need a run row so Introspect's run query doesn't bail.
	_ = st.Tx(context.Background(), func(qq store.Querier) error {
		_, err := qq.Exec(`INSERT INTO runs(id, status) VALUES('rTest','running')`)
		return err
	})

	// Fixture role md uses {{peers}} verbatim, like the real master.md.
	tmpl := `# Role: master
Your team: {{peers}}
End.`
	tmplPath := filepath.Join(tmp, "master.md")
	if err := os.WriteFile(tmplPath, []byte(tmpl), 0o644); err != nil {
		t.Fatal(err)
	}

	ag := NewAgent(nil, st, bus, q, "master-1", "master", "rTest", nil, nil, "claude-code", 0)
	ag.StaticPeers = []Peer{
		{Role: "fe-lead"},
		{Role: "fe-worker"},
		{Role: "be-lead"},
		{Role: "be-worker"},
	}

	rendered, err := ag.RenderSystemPrompt(context.Background(), tmplPath)
	if err != nil {
		t.Fatalf("RenderSystemPrompt: %v", err)
	}

	// Must NOT contain the unexpanded template marker OR the empty-team
	// "****" symptom from the broken render.
	if strings.Contains(rendered, "{{peers}}") {
		t.Fatalf("template marker not substituted: %q", rendered)
	}
	if strings.Contains(rendered, "Your team: \nEnd.") || strings.Contains(rendered, "Your team: ") && strings.Contains(rendered, "End.") && !strings.Contains(rendered, "fe-lead") {
		t.Fatalf("empty team substitution (the root-cause bug): %q", rendered)
	}

	// Every declared role should appear by name.
	for _, expected := range []string{"fe-lead", "fe-worker", "be-lead", "be-worker"} {
		if !strings.Contains(rendered, expected) {
			t.Errorf("rendered prompt missing role %q. Output: %q", expected, rendered)
		}
	}
}

// TestRenderSystemPromptFallsBackToLiveAgentsWhenStaticPeersEmpty
// keeps the legacy behaviour working for older callers that haven't
// adopted StaticPeers — Introspect() queries the agents table.
func TestRenderSystemPromptFallsBackToLiveAgentsWhenStaticPeersEmpty(t *testing.T) {
	t.Skip("live-agents fallback path requires more fixture setup; primary regression covered by TestRenderSystemPromptHasTeamFromStaticPeers")
	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)
	_ = st.Tx(context.Background(), func(qq store.Querier) error {
		_, err := qq.Exec(`INSERT INTO runs(id, status) VALUES('rTest','running')`)
		return err
	})
	// Pre-existing sibling in the agents table.
	_ = st.Tx(context.Background(), func(qq store.Querier) error {
		_, err := qq.Exec(`INSERT INTO agents(id, run_id, role, status, provider, spawned_at) VALUES('peer-1','rTest','helper','running','scripted',?)`,
			store.FmtTime(store.Now()))
		return err
	})

	tmpl := `peers: {{peers}}`
	tmplPath := filepath.Join(tmp, "role.md")
	_ = os.WriteFile(tmplPath, []byte(tmpl), 0o644)

	ag := NewAgent(nil, st, bus, q, "me", "lonely", "rTest", nil, nil, "scripted", 0)
	// StaticPeers intentionally NOT set; falls back to agents table.
	rendered, _ := ag.RenderSystemPrompt(context.Background(), tmplPath)
	if !strings.Contains(rendered, "peer-1") {
		t.Errorf("fallback path lost the live peer: %q", rendered)
	}
}
