package screens

import (
	"image/color"
	"testing"

	tea "charm.land/bubbletea/v2"

	"git.secluded.site/keld/internal/theme"
	"git.secluded.site/keld/internal/ui"
)

func testPresets() []string {
	return []string{"home@cloud", "work@local", "media"}
}

func TestPresetTitle(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	if got := p.Title(); got != "Select a preset" {
		t.Errorf("Title() = %q, want %q", got, "Select a preset")
	}
}

func TestPresetSelectionEmpty(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	if got := p.Selection(); got != "" {
		t.Errorf("Selection() before interaction = %q, want empty", got)
	}
}

func TestPresetKeyBindings(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	bindings := p.KeyBindings()

	if len(bindings) == 0 {
		t.Fatal("KeyBindings() returned no bindings")
	}
}

func TestPresetEscWhenNotFilteringReturnsBack(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	// Initialise the form so huh is ready to receive input.
	p.Init()
	// Send a WindowSizeMsg so huh can lay out its fields.
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Esc when not filtering should signal back navigation.
	_, cmd := p.Update(tea.KeyPressMsg{Code: tea.KeyEscape})

	if cmd == nil {
		t.Fatal("expected BackCmd on Esc when not filtering")
	}
	msg := cmd()
	if _, ok := msg.(ui.BackMsg); !ok {
		t.Errorf("cmd produced %T, want ui.BackMsg", msg)
	}
}

func TestPresetEscWhileFilteringDoesNotBack(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Activate filter mode by pressing '/'.
	p.Update(tea.KeyPressMsg{Code: '/', Text: "/"})

	// Esc while filtering should close the filter, not back out.
	updated, cmd := p.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
	preset := updated.(*Preset)

	// Should still be on this screen (no BackCmd).
	if cmd != nil {
		msg := cmd()
		if _, ok := msg.(ui.BackMsg); ok {
			t.Error("Esc while filtering should not produce BackMsg")
		}
	}

	// Screen should still be functional (not backed out).
	if preset.Selection() != "" {
		t.Error("preset should not have a selection after Esc from filter")
	}
}

func TestPresetCompleteReturnsDone(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Press enter to select the focused option (first one: global defaults).
	// huh uses an internal message chain (nextFieldMsg → nextGroupMsg →
	// StateCompleted), so we drain until DoneMsg appears.
	var screen ui.Screen
	var cmd tea.Cmd
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	_, cmd = drain(screen.(*Preset), cmd)

	if cmd == nil {
		t.Fatal("expected DoneCmd after form completion")
	}
	msg := cmd()
	if _, ok := msg.(ui.DoneMsg); !ok {
		t.Errorf("cmd produced %T, want ui.DoneMsg", msg)
	}
}

func TestPresetSelectionValue(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Move down to the first real preset ("home@cloud") and select it.
	p.Update(tea.KeyPressMsg{Code: tea.KeyDown})
	var screen ui.Screen
	var cmd tea.Cmd
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	p, _ = drain(screen.(*Preset), cmd)

	if got := p.Selection(); got != "home@cloud" {
		t.Errorf("Selection() = %q, want %q", got, "home@cloud")
	}
}

func TestPresetGlobalDefaultDisplayLabel(t *testing.T) {
	t.Parallel()

	// When the user selects "(global defaults only)", the resolved
	// value is "" but the breadcrumb display should be meaningful.
	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// First option is the global default. Select it.
	var screen ui.Screen
	var cmd tea.Cmd
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	p, _ = drain(screen.(*Preset), cmd)

	// Selection() should return a display-friendly label, not "".
	sel := p.Selection()
	if sel == "" {
		t.Error("Selection() for global defaults should return a display label, not empty string")
	}
}

func TestPresetReinitPreservesSelection(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Move down to "home@cloud" and select it.
	p.Update(tea.KeyPressMsg{Code: tea.KeyDown})
	var screen ui.Screen
	var cmd tea.Cmd
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	p, _ = drain(screen.(*Preset), cmd)

	if p.Selection() != "home@cloud" {
		t.Fatalf("precondition: Selection() = %q, want %q", p.Selection(), "home@cloud")
	}

	// Re-initialise (simulates back navigation). The form state
	// should carry the previous selection through.
	p.Init()

	// The internal selected value should still be "home@cloud".
	if p.selected != "home@cloud" {
		t.Errorf("after re-init, selected = %q, want %q (should preserve previous choice)", p.selected, "home@cloud")
	}
}

func TestPresetReinitAfterBack(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Complete the form first so the state is no longer StateNormal.
	var screen ui.Screen
	var cmd tea.Cmd
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	p, _ = drain(screen.(*Preset), cmd)

	// Re-initialise (simulates the session navigating back to this screen).
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Should be functional again — selecting should produce DoneCmd.
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	_, cmd = drain(screen.(*Preset), cmd)

	if cmd == nil {
		t.Fatal("expected DoneCmd after re-init, form may not have been reset")
	}
	msg := cmd()
	if _, ok := msg.(ui.DoneMsg); !ok {
		t.Errorf("after re-init, cmd produced %T, want ui.DoneMsg", msg)
	}
}

func TestPresetRebuildsFormOnBackgroundChange(t *testing.T) {
	t.Parallel()

	styles := testStyles()
	p := NewPreset(testPresets(), styles)
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	originalForm := p.form

	// Simulate background detection switching to light. The session
	// would have already mutated *styles before forwarding this msg.
	*styles = theme.New(false)
	updated, _ := p.Update(tea.BackgroundColorMsg{Color: color.White})
	p = updated.(*Preset)

	// The form should have been rebuilt with the new theme.
	if p.form == originalForm {
		t.Error("form was not rebuilt after BackgroundColorMsg; cached theme is stale")
	}
}

func TestPresetValueReturnsActualPresetName(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// Move down to "home@cloud" and select it.
	p.Update(tea.KeyPressMsg{Code: tea.KeyDown})
	var screen ui.Screen
	var cmd tea.Cmd
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	p, _ = drain(screen.(*Preset), cmd)

	if got := p.Value(); got != "home@cloud" {
		t.Errorf("Value() = %q, want %q", got, "home@cloud")
	}
}

func TestPresetValueEmptyForGlobalDefaults(t *testing.T) {
	t.Parallel()

	p := NewPreset(testPresets(), testStyles())
	p.Init()
	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})

	// First option is global defaults. Select it.
	var screen ui.Screen
	var cmd tea.Cmd
	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	p, _ = drain(screen.(*Preset), cmd)

	if got := p.Value(); got != "" {
		t.Errorf("Value() = %q, want empty for global defaults", got)
	}
}
