package theme

import (
	"testing"

	"github.com/charmbracelet/lipgloss"
)

func strPtr(value string) *string {
	return &value
}

func TestDefaultThemeEditorHintBoxUsesEditorFocusColor(t *testing.T) {
	th := DefaultTheme()
	if got := th.EditorHintBox.GetBorderLeftForeground(); got != th.PaneBorderFocusEditor {
		t.Fatalf("expected editor hint border to use editor focus color, got %v", got)
	}
}

func TestApplySpecOverridesColorsAndMetadata(t *testing.T) {
	base := DefaultTheme()
	spec := ThemeSpec{
		Colors: ColorsSpec{
			PaneBorderFocusEditor:   strPtr("#654321"),
			PaneBorderFocusResponse: strPtr("#fedcba"),
			PaneActiveForeground:    strPtr("#123456"),
			ModalBackdrop:           strPtr("#202020"),
			ModalInputBackground:    strPtr("#181818"),
			ModalOption:             strPtr("#778899"),
			GitModified:             strPtr("#445500"),
			GitBranch:               strPtr("#445566"),
		},
		HeaderSegments: []HeaderSegmentSpec{{
			Background: strPtr("#111111"),
			Foreground: strPtr("#eeeeee"),
		}},
		CommandSegments: []CommandSegmentSpec{{
			Key:  strPtr("#abcdef"),
			Text: strPtr("#fedcba"),
		}},
		StatusBar: &StatusBarSpec{
			Base: strPtr("#010101"),
			Info: &StatusBarSegmentSpec{
				Foreground: strPtr("#fafafa"),
				Background: strPtr("#020202"),
			},
			Host: &StatusBarSegmentSpec{
				Background: strPtr("#030303"),
			},
		},
		EditorMetadata: &EditorMetadataSpec{
			CommentMarker: strPtr("#222222"),
			DirectiveColors: map[string]string{
				"custom": "#333333",
			},
		},
		Styles: StylesSpec{
			ListItemTitle:       &StyleSpec{Foreground: strPtr("#222233")},
			ListItemDescription: &StyleSpec{Foreground: strPtr("#9999aa")},
			CLIRunPickerSelected: &StyleSpec{
				Foreground: strPtr("#111827"),
				Background: strPtr("#facc15"),
			},
			CLIRunPickerCursorSelected: &StyleSpec{Foreground: strPtr("#22c55e")},
			ResponseContentRaw:         &StyleSpec{Foreground: strPtr("#abcdef")},
			ResponseSelection:          &StyleSpec{Background: strPtr("#101010")},
			ResponseCursor:             &StyleSpec{Foreground: strPtr("#010203")},
			ExplainLabel:               &StyleSpec{Foreground: strPtr("#bada55")},
			ExplainBadgeError:          &StyleSpec{Background: strPtr("#220000")},
			ExplainWarning:             &StyleSpec{Foreground: strPtr("#ffaa00")},
			PaneTitleEditor:            &StyleSpec{Foreground: strPtr("#445566")},
			PaneTitleResponse:          &StyleSpec{Foreground: strPtr("#665544")},
			StreamContent:              &StyleSpec{Foreground: strPtr("#123123")},
		},
	}

	updated, err := ApplySpec(base, spec)
	if err != nil {
		t.Fatalf("ApplySpec returned error: %v", err)
	}

	if got := updated.PaneActiveForeground; got != "#123456" {
		t.Errorf("expected pane active foreground %q, got %q", "#123456", got)
	}
	if got := updated.PaneBorderFocusEditor; got != "#654321" {
		t.Errorf("expected editor focus border %q, got %q", "#654321", got)
	}
	if got := updated.PaneBorderFocusResponse; got != "#fedcba" {
		t.Errorf("expected response focus border %q, got %q", "#fedcba", got)
	}
	if got := updated.ModalBackdrop; got != "#202020" {
		t.Errorf("expected modal backdrop %q, got %q", "#202020", got)
	}
	if got := updated.ModalInputBackground; got != "#181818" {
		t.Errorf("expected modal input background %q, got %q", "#181818", got)
	}
	if got := updated.ModalOption; got != "#778899" {
		t.Errorf("expected modal option %q, got %q", "#778899", got)
	}
	if got := updated.GitColors.Modified; got != "#445500" {
		t.Errorf("expected git modified color %q, got %q", "#445500", got)
	}
	if got := updated.GitColors.Branch; got != "#445566" {
		t.Errorf("expected git branch color %q, got %q", "#445566", got)
	}
	if len(updated.HeaderSegments) != 1 {
		t.Fatalf("expected 1 header segment, got %d", len(updated.HeaderSegments))
	}
	if updated.HeaderSegments[0].Background != "#111111" {
		t.Errorf(
			"expected header background override, got %q",
			updated.HeaderSegments[0].Background,
		)
	}
	if len(updated.CommandSegments) != 1 {
		t.Fatalf("expected 1 command segment, got %d", len(updated.CommandSegments))
	}
	if updated.CommandSegments[0].Key != "#abcdef" {
		t.Errorf("expected command key color override, got %q", updated.CommandSegments[0].Key)
	}
	if got := updated.StatusBarPalette.Base; got != "#010101" {
		t.Errorf("expected status bar base override, got %q", got)
	}
	if got := updated.StatusBarPalette.Info.Foreground; got != "#fafafa" {
		t.Errorf("expected status bar info foreground override, got %q", got)
	}
	if got := updated.StatusBarPalette.Info.Background; got != "#020202" {
		t.Errorf("expected status bar info background override, got %q", got)
	}
	if got := updated.StatusBarPalette.Host.Background; got != "#030303" {
		t.Errorf("expected status bar host background override, got %q", got)
	}
	if got := updated.StatusBarPalette.Host.Foreground; got != base.StatusBarPalette.Host.Foreground {
		t.Errorf("expected status bar host foreground fallback, got %q", got)
	}
	if updated.EditorMetadata.CommentMarker != "#222222" {
		t.Errorf(
			"expected metadata comment marker override, got %q",
			updated.EditorMetadata.CommentMarker,
		)
	}
	if color, ok := updated.EditorMetadata.DirectiveColors["custom"]; !ok || color != "#333333" {
		t.Errorf("expected directive color #333333, got %q (present=%v)", color, ok)
	}
	if color := updated.ListItemTitle.GetForeground(); color != lipgloss.Color("#222233") {
		t.Errorf("expected list item title foreground #222233, got %v", color)
	}
	if color := updated.ListItemDescription.GetForeground(); color != lipgloss.Color("#9999aa") {
		t.Errorf("expected list item description foreground #9999aa, got %v", color)
	}
	if color := updated.CLIRunPickerSelected.GetForeground(); color != lipgloss.Color("#111827") {
		t.Errorf("expected CLI run picker foreground #111827, got %v", color)
	}
	if color := updated.CLIRunPickerSelected.GetBackground(); color != lipgloss.Color("#facc15") {
		t.Errorf("expected CLI run picker background #facc15, got %v", color)
	}
	if color := updated.CLIRunPickerCursorSelected.GetForeground(); color != lipgloss.Color(
		"#22c55e",
	) {
		t.Errorf("expected CLI run picker cursor foreground #22c55e, got %v", color)
	}
	if color := updated.ResponseContentRaw.GetForeground(); color != lipgloss.Color("#abcdef") {
		t.Errorf("expected raw response foreground #abcdef, got %v", color)
	}
	if color := updated.ResponseSelection.GetBackground(); color != lipgloss.Color("#101010") {
		t.Errorf("expected response selection background #101010, got %v", color)
	}
	if color := updated.ResponseCursor.GetForeground(); color != lipgloss.Color("#010203") {
		t.Errorf("expected response cursor foreground #010203, got %v", color)
	}
	if color := updated.ExplainLabel.GetForeground(); color != lipgloss.Color("#bada55") {
		t.Errorf("expected explain label foreground #bada55, got %v", color)
	}
	if color := updated.ExplainBadgeError.GetBackground(); color != lipgloss.Color("#220000") {
		t.Errorf("expected explain badge error background #220000, got %v", color)
	}
	if color := updated.ExplainWarning.GetForeground(); color != lipgloss.Color("#ffaa00") {
		t.Errorf("expected explain warning foreground #ffaa00, got %v", color)
	}
	if color := updated.PaneTitleEditor.GetForeground(); color != lipgloss.Color("#445566") {
		t.Errorf("expected editor pane title foreground #445566, got %v", color)
	}
	if color := updated.PaneTitleResponse.GetForeground(); color != lipgloss.Color("#665544") {
		t.Errorf("expected response pane title foreground #665544, got %v", color)
	}
	if color := updated.StreamContent.GetForeground(); color != lipgloss.Color("#123123") {
		t.Errorf("expected stream content foreground #123123, got %v", color)
	}
	if base.PaneActiveForeground == "#123456" {
		t.Errorf("base theme should remain unchanged")
	}
}

func TestApplySpecRejectsEmptyStatusBarColor(t *testing.T) {
	_, err := ApplySpec(DefaultTheme(), ThemeSpec{
		StatusBar: &StatusBarSpec{
			Info: &StatusBarSegmentSpec{Background: strPtr("")},
		},
	})
	if err == nil {
		t.Fatal("expected empty status bar color to fail")
	}
	if got := err.Error(); got != "status_bar.info.background: colour value may not be empty" {
		t.Fatalf("unexpected error %q", got)
	}
}

func TestApplySpecDirectiveDefaultOverridesExistingEntries(t *testing.T) {
	base := DefaultTheme()
	original := base.EditorMetadata.DirectiveColors
	if len(original) == 0 {
		t.Fatalf("default theme should define directive colors")
	}
	if original["name"] == "" {
		t.Fatalf("expected default directive color for name")
	}

	spec := ThemeSpec{
		EditorMetadata: &EditorMetadataSpec{
			DirectiveDefault: strPtr("#123456"),
			DirectiveColors: map[string]string{
				"tag": "#abcdef",
			},
		},
	}

	updated, err := ApplySpec(base, spec)
	if err != nil {
		t.Fatalf("ApplySpec returned error: %v", err)
	}

	if updated.EditorMetadata.DirectiveDefault != "#123456" {
		t.Fatalf(
			"expected directive default to update, got %q",
			updated.EditorMetadata.DirectiveDefault,
		)
	}
	if color := updated.EditorMetadata.DirectiveColors["name"]; color != "#123456" {
		t.Errorf("expected name directive to follow new default, got %q", color)
	}
	if color := updated.EditorMetadata.DirectiveColors["tag"]; color != "#abcdef" {
		t.Errorf("expected explicit tag directive override, got %q", color)
	}
	if base.EditorMetadata.DirectiveDefault == "#123456" {
		t.Errorf("base theme should remain unchanged")
	}
	if color := base.EditorMetadata.DirectiveColors["name"]; color != original["name"] {
		t.Errorf("base directive colors should remain unchanged")
	}
}

func TestApplySpecRTSKeywordOverrides(t *testing.T) {
	base := DefaultTheme()
	spec := ThemeSpec{
		EditorMetadata: &EditorMetadataSpec{
			RTSKeywordDefault: strPtr("#101010"),
			RTSKeywordDecl:    strPtr("#111111"),
			RTSKeywordControl: strPtr("#222222"),
			RTSKeywordLiteral: strPtr("#333333"),
			RTSKeywordLogical: strPtr("#444444"),
		},
	}

	updated, err := ApplySpec(base, spec)
	if err != nil {
		t.Fatalf("ApplySpec returned error: %v", err)
	}

	if got := updated.EditorMetadata.RTSKeywordDefault; got != "#101010" {
		t.Errorf("expected RTS keyword default %q, got %q", "#101010", got)
	}
	if got := updated.EditorMetadata.RTSKeywordDecl; got != "#111111" {
		t.Errorf("expected RTS keyword decl %q, got %q", "#111111", got)
	}
	if got := updated.EditorMetadata.RTSKeywordControl; got != "#222222" {
		t.Errorf("expected RTS keyword control %q, got %q", "#222222", got)
	}
	if got := updated.EditorMetadata.RTSKeywordLiteral; got != "#333333" {
		t.Errorf("expected RTS keyword literal %q, got %q", "#333333", got)
	}
	if got := updated.EditorMetadata.RTSKeywordLogical; got != "#444444" {
		t.Errorf("expected RTS keyword logical %q, got %q", "#444444", got)
	}
	if base.EditorMetadata.RTSKeywordDefault == "#101010" {
		t.Errorf("base theme should remain unchanged")
	}
}
