package switcher

import (
	"testing"
	"time"

	"forge.secuaas.ovh/olivier/claude-failover/internal/config"
	"forge.secuaas.ovh/olivier/claude-failover/internal/quota"
	"forge.secuaas.ovh/olivier/claude-failover/internal/state"
)

// mockTmux for switcher tests.
type mockTmux struct {
	sessions    map[string]bool
	paneOutput  map[string]string
	killCalls   []string
	createCalls []string
}

func newMockTmux() *mockTmux {
	return &mockTmux{
		sessions:   make(map[string]bool),
		paneOutput: make(map[string]string),
	}
}

func (m *mockTmux) HasSession(name string) bool { return m.sessions[name] }
func (m *mockTmux) CreateSession(name, _ string) error {
	m.sessions[name] = true
	m.createCalls = append(m.createCalls, name)
	return nil
}
func (m *mockTmux) KillSession(name string) error {
	delete(m.sessions, name)
	m.killCalls = append(m.killCalls, name)
	return nil
}
func (m *mockTmux) SendKeys(_, _ string) error { return nil }
func (m *mockTmux) CapturePaneTail(session string, _ int) (string, error) {
	return m.paneOutput[session], nil
}

// TestFindTargetAccount returns the first account that differs from current.
func TestFindTargetAccount(t *testing.T) {
	tc := newMockTmux()
	s := state.New("")
	cfg := &config.Config{
		Accounts: []config.AccountConfig{
			{Name: "compte1", Priority: 1},
			{Name: "compte2", Priority: 2},
		},
	}
	a := New(tc, s, cfg, make(chan quota.SwitchRequest), nil)

	target := a.findTargetAccount("compte1")
	if target == nil || target.Name != "compte2" {
		t.Errorf("expected compte2, got %v", target)
	}
}

// TestFindTargetAccountSingleAccount returns nil when only one account exists.
func TestFindTargetAccountSingleAccount(t *testing.T) {
	tc := newMockTmux()
	s := state.New("")
	cfg := &config.Config{
		Accounts: []config.AccountConfig{{Name: "solo"}},
	}
	a := New(tc, s, cfg, make(chan quota.SwitchRequest), nil)

	if got := a.findTargetAccount("solo"); got != nil {
		t.Errorf("expected nil for single account, got %v", got)
	}
}

// TestExtractResumeUUID parses UUID from pane output.
func TestExtractResumeUUID(t *testing.T) {
	input := "$ claude --resume a1b2c3d4-e5f6-7890-abcd-ef1234567890 --model sonnet"
	got := extractResumeUUID(input)
	want := "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
	if got != want {
		t.Errorf("expected %q, got %q", want, got)
	}
}

// TestExtractResumeUUIDMissing returns empty string when no UUID present.
func TestExtractResumeUUIDMissing(t *testing.T) {
	if got := extractResumeUUID("no uuid here"); got != "" {
		t.Errorf("expected empty, got %q", got)
	}
}

// TestTimeUntilReset parses minute and hour formats correctly.
func TestTimeUntilReset(t *testing.T) {
	cases := []struct {
		input string
		want  time.Duration
	}{
		{"in 45 minutes", 45 * time.Minute},
		{"in 2 hours", 2 * time.Hour},
		{"in 1 hour", 1 * time.Hour},
		{"", 2 * time.Hour},
		{"8pm", 2 * time.Hour}, // fallback for unrecognised formats
	}
	for _, c := range cases {
		if got := timeUntilReset(c.input); got != c.want {
			t.Errorf("timeUntilReset(%q) = %v, want %v", c.input, got, c.want)
		}
	}
}

// TestKillAndRecreatePoolSessions verifies that executeSwitch restarts sessions.
func TestKillAndRecreatePoolSessions(t *testing.T) {
	tc := newMockTmux()
	tc.sessions["ccl-auto-0"] = true
	tc.sessions["ccl-auto-1"] = true
	tc.sessions["dedicated-1"] = true

	s := state.New("")
	s.SetActiveAccount("compte1")

	cfg := &config.Config{
		Accounts: []config.AccountConfig{
			{Name: "compte1", Home: t.TempDir()},
			{Name: "compte2", Home: t.TempDir()},
		},
		Pool: config.PoolConfig{
			Dedicated:  []config.DedicatedSession{{Name: "dedicated-1", Project: "/tmp"}},
			Autonomous: config.AutonomousConfig{Prefix: "ccl-auto-", Min: 2, Max: 2},
		},
	}

	a := New(tc, s, cfg, make(chan quota.SwitchRequest), nil)
	// CRITICAL: isolate symlink manipulation in a tmpdir so the test never
	// touches the real ~/.claude (regression: a reboot used to leave Claude
	// Code unusable because the test had repointed ~/.claude to /tmp/...).
	a.homeDir = t.TempDir()
	a.executeSwitch(quota.SwitchRequest{From: "compte1"})

	// Active account must have changed.
	if got := s.ActiveAccount(); got != "compte2" {
		t.Errorf("expected active account compte2, got %q", got)
	}

	// All old sessions must have been killed.
	for _, name := range []string{"ccl-auto-0", "ccl-auto-1", "dedicated-1"} {
		found := false
		for _, k := range tc.killCalls {
			if k == name {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("expected %q to be killed", name)
		}
	}

	// Min pool sessions must be recreated.
	recreated := map[string]bool{}
	for _, c := range tc.createCalls {
		recreated[c] = true
	}
	if !recreated["ccl-auto-0"] || !recreated["ccl-auto-1"] {
		t.Errorf("expected autonomous sessions recreated; createCalls=%v", tc.createCalls)
	}
}
