package tui

import (
	"strings"
	"testing"
)

// ── TestMenuOptions ─────────────────────────────────────────────────────

func TestTestMenuOptions_Count(t *testing.T) {
	opts := TestMenuOptions()
	if len(opts) != 4 {
		t.Fatalf("expected 4 test menu options, got %d", len(opts))
	}
}

func TestTestMenuOptions_Labels(t *testing.T) {
	opts := TestMenuOptions()
	expected := []string{"Go Tests", "E2E Tests", "E2E Tests (headed)", "All Tests"}
	for i, want := range expected {
		if opts[i].Label != want {
			t.Errorf("option[%d]: expected label %q, got %q", i, want, opts[i].Label)
		}
	}
}

func TestMainMenuOptions_IncludesTests(t *testing.T) {
	opts := MainMenuOptions()
	found := false
	for _, opt := range opts {
		if opt.Label == "Tests" {
			found = true
			break
		}
	}
	if !found {
		t.Error("MainMenuOptions should include 'Tests'")
	}
}

func TestMainMenuOptions_TestsBeforeQuit(t *testing.T) {
	opts := MainMenuOptions()
	testsIdx, quitIdx := -1, -1
	for i, opt := range opts {
		if opt.Label == "Tests" {
			testsIdx = i
		}
		if opt.Label == "Quit" {
			quitIdx = i
		}
	}
	if testsIdx < 0 || quitIdx < 0 {
		t.Fatal("expected both Tests and Quit in menu")
	}
	if testsIdx >= quitIdx {
		t.Errorf("Tests (index %d) should come before Quit (index %d)", testsIdx, quitIdx)
	}
}

func TestServerMenuOptions_NoRunTests(t *testing.T) {
	for _, running := range []bool{true, false} {
		opts := ServerMenuOptions(running)
		for _, opt := range opts {
			if opt.Label == "Run Tests" {
				t.Errorf("ServerMenuOptions(running=%v) should not have 'Run Tests', found it", running)
			}
		}
	}
}

func TestServerMenuOptions_HasTests(t *testing.T) {
	for _, running := range []bool{true, false} {
		opts := ServerMenuOptions(running)
		found := false
		for _, opt := range opts {
			if opt.Label == "Tests" {
				found = true
			}
		}
		if !found {
			t.Errorf("ServerMenuOptions(running=%v) should have 'Tests'", running)
		}
	}
}

// ── updateTestsScreen ───────────────────────────────────────────────────

func TestUpdateTestsScreen_Navigation(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 0, WindowHeight: 40}

	m, _ = Update(m, KeyMsg{Str: "down"})
	if m.Cursor != 1 {
		t.Errorf("expected cursor 1 after down, got %d", m.Cursor)
	}

	m, _ = Update(m, KeyMsg{Str: "up"})
	if m.Cursor != 0 {
		t.Errorf("expected cursor 0 after up, got %d", m.Cursor)
	}

	// Can't go below 0
	m, _ = Update(m, KeyMsg{Str: "up"})
	if m.Cursor != 0 {
		t.Errorf("expected cursor 0 clamped, got %d", m.Cursor)
	}
}

func TestUpdateTestsScreen_Back(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 2}
	m, _ = Update(m, KeyMsg{Str: "0"})
	if m.Screen != ScreenMain {
		t.Errorf("expected ScreenMain, got %d", m.Screen)
	}
	if m.Cursor != 0 {
		t.Errorf("expected cursor reset to 0, got %d", m.Cursor)
	}
}

func TestUpdateTestsScreen_SelectGoTests(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 0}
	m, intents := Update(m, KeyMsg{Str: "enter"})
	if len(intents) != 1 || intents[0].Kind != IntentRunGoTests {
		t.Errorf("expected IntentRunGoTests, got %v", intents)
	}
	if m.LastTestKind != TestKindGo {
		t.Errorf("expected LastTestKind=TestKindGo, got %d", m.LastTestKind)
	}
}

func TestUpdateTestsScreen_SelectE2ETests(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 1}
	_, intents := Update(m, KeyMsg{Str: "enter"})
	if len(intents) != 1 || intents[0].Kind != IntentRunE2ETests {
		t.Errorf("expected IntentRunE2ETests, got %v", intents)
	}
}

func TestUpdateTestsScreen_SelectE2EHeaded(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 2}
	_, intents := Update(m, KeyMsg{Str: "enter"})
	if len(intents) != 1 || intents[0].Kind != IntentRunE2EHeadTests {
		t.Errorf("expected IntentRunE2EHeadTests, got %v", intents)
	}
}

func TestUpdateTestsScreen_SelectAllTests(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 3}
	_, intents := Update(m, KeyMsg{Str: "enter"})
	if len(intents) != 1 || intents[0].Kind != IntentRunAllTests {
		t.Errorf("expected IntentRunAllTests, got %v", intents)
	}
}

// ── MsgTestsDone ────────────────────────────────────────────────────────

func TestMsgTestsDone_SetsKindAndStatus(t *testing.T) {
	m := Model{Screen: ScreenTests}
	m, _ = Update(m, MsgTestsDone{Kind: TestKindE2E, Passed: true, Output: []string{"ok"}})
	if m.Screen != ScreenTestOutput {
		t.Errorf("expected ScreenTestOutput, got %d", m.Screen)
	}
	if m.LastTestKind != TestKindE2E {
		t.Errorf("expected LastTestKind=TestKindE2E, got %d", m.LastTestKind)
	}
	if !strings.Contains(m.StatusMsg, "PASSED") {
		t.Errorf("expected PASSED in status, got %q", m.StatusMsg)
	}
	if !strings.Contains(m.StatusMsg, "E2E") {
		t.Errorf("expected E2E in status, got %q", m.StatusMsg)
	}
}

func TestMsgTestsDone_Failed(t *testing.T) {
	m := Model{}
	m, _ = Update(m, MsgTestsDone{Kind: TestKindGo, Passed: false, Output: []string{"FAIL"}})
	if !strings.Contains(m.StatusMsg, "FAILED") {
		t.Errorf("expected FAILED in status, got %q", m.StatusMsg)
	}
}

// ── updateTestOutput ────────────────────────────────────────────────────

func TestUpdateTestOutput_BackGoesToTests(t *testing.T) {
	m := Model{Screen: ScreenTestOutput, LastTestKind: TestKindGo}
	m, _ = Update(m, KeyMsg{Str: "0"})
	if m.Screen != ScreenTests {
		t.Errorf("expected ScreenTests, got %d", m.Screen)
	}
}

func TestUpdateTestOutput_RerunUsesLastKind(t *testing.T) {
	m := Model{Screen: ScreenTestOutput, LastTestKind: TestKindE2E, WindowHeight: 40}
	_, intents := Update(m, KeyMsg{Str: "r"})
	if len(intents) != 1 || intents[0].Kind != IntentRunE2ETests {
		t.Errorf("expected IntentRunE2ETests, got %v", intents)
	}
}

func TestUpdateTestOutput_RerunDefaultsToGo(t *testing.T) {
	m := Model{Screen: ScreenTestOutput, LastTestKind: TestKindNone, WindowHeight: 40}
	_, intents := Update(m, KeyMsg{Str: "r"})
	if len(intents) != 1 || intents[0].Kind != IntentRunGoTests {
		t.Errorf("expected IntentRunGoTests as default, got %v", intents)
	}
}

// ── viewTests ───────────────────────────────────────────────────────────

func TestViewTests_ShowsOptions(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 0, WindowWidth: 80, WindowHeight: 40}
	out := View(m)
	if !strings.Contains(out, "Go Tests") {
		t.Error("expected 'Go Tests' in view")
	}
	if !strings.Contains(out, "E2E Tests") {
		t.Error("expected 'E2E Tests' in view")
	}
	if !strings.Contains(out, "All Tests") {
		t.Error("expected 'All Tests' in view")
	}
}

func TestViewTests_ShowsCursor(t *testing.T) {
	m := Model{Screen: ScreenTests, Cursor: 1, WindowWidth: 80, WindowHeight: 40}
	out := View(m)
	// Cursor on E2E Tests (index 1)
	lines := strings.Split(out, "\n")
	foundCursor := false
	for _, line := range lines {
		if strings.Contains(line, "> ") && strings.Contains(line, "E2E Tests") && !strings.Contains(line, "(headed)") {
			foundCursor = true
		}
	}
	if !foundCursor {
		t.Error("expected cursor on E2E Tests")
	}
}

func TestViewTests_ShowsLastRun(t *testing.T) {
	m := Model{
		Screen:       ScreenTests,
		Cursor:       0,
		WindowWidth:  80,
		WindowHeight: 40,
		LastTestKind: TestKindGo,
		StatusMsg:    "Go Tests PASSED",
	}
	out := View(m)
	if !strings.Contains(out, "Last run:") {
		t.Error("expected 'Last run:' in view")
	}
	if !strings.Contains(out, "PASSED") {
		t.Error("expected PASSED in last run")
	}
}

func TestViewTestOutput_ShowsKindInTitle(t *testing.T) {
	m := Model{
		Screen:       ScreenTestOutput,
		LastTestKind: TestKindE2E,
		StatusMsg:    "E2E Tests PASSED",
		LogLines:     []string{"ok"},
		WindowWidth:  80,
		WindowHeight: 40,
	}
	out := View(m)
	if !strings.Contains(out, "Test Results — E2E Tests") {
		t.Errorf("expected 'Test Results — E2E Tests' in view, got:\n%s", out)
	}
}

// ── Main menu Tests navigation ──────────────────────────────────────────

func TestMainMenu_TestsNavigation(t *testing.T) {
	m := Model{Screen: ScreenMain, Cursor: 2} // Tests is at index 2
	m, intents := Update(m, KeyMsg{Str: "enter"})
	if m.Screen != ScreenTests {
		t.Errorf("expected ScreenTests, got %d", m.Screen)
	}
	if len(intents) != 0 {
		t.Errorf("expected no intents for Tests nav, got %v", intents)
	}
}

// ── Server menu Tests navigation ────────────────────────────────────────

func TestServerMenu_TestsNavigation(t *testing.T) {
	// "Tests" is the last option in both running/stopped menus
	opts := ServerMenuOptions(false)
	testsIdx := -1
	for i, o := range opts {
		if o.Label == "Tests" {
			testsIdx = i
		}
	}
	if testsIdx < 0 {
		t.Fatal("Tests not found in server menu")
	}

	m := Model{Screen: ScreenServer, Cursor: testsIdx}
	m, _ = Update(m, KeyMsg{Str: "enter"})
	if m.Screen != ScreenTests {
		t.Errorf("expected ScreenTests, got %d", m.Screen)
	}
}

// ── testKindLabel ───────────────────────────────────────────────────────

func TestTestKindLabel(t *testing.T) {
	cases := []struct {
		kind TestKind
		want string
	}{
		{TestKindGo, "Go Tests"},
		{TestKindE2E, "E2E Tests"},
		{TestKindE2EHead, "E2E Tests (headed)"},
		{TestKindAll, "All Tests"},
		{TestKindNone, "Tests"},
	}
	for _, tc := range cases {
		got := testKindLabel(tc.kind)
		if got != tc.want {
			t.Errorf("testKindLabel(%d) = %q, want %q", tc.kind, got, tc.want)
		}
	}
}

// ── testKindIntent ──────────────────────────────────────────────────────

func TestTestKindIntent(t *testing.T) {
	cases := []struct {
		kind TestKind
		want IntentKind
	}{
		{TestKindGo, IntentRunGoTests},
		{TestKindE2E, IntentRunE2ETests},
		{TestKindE2EHead, IntentRunE2EHeadTests},
		{TestKindAll, IntentRunAllTests},
		{TestKindNone, ""},
	}
	for _, tc := range cases {
		got := testKindIntent(tc.kind)
		if got != tc.want {
			t.Errorf("testKindIntent(%d) = %q, want %q", tc.kind, got, tc.want)
		}
	}
}
