package screens

import (
	"strings"
	"testing"

	tea "charm.land/bubbletea/v2"

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

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	if got := c.Title(); got != "Confirm execution" {
		t.Errorf("Title() = %q, want %q", got, "Confirm execution")
	}
}

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	if got := c.Selection(); got != "" {
		t.Errorf("Selection() before interaction = %q, want empty", got)
	}
}

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	bindings := c.KeyBindings()

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

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

	called := false
	c := NewConfirm(func() string {
		called = true
		return "command: restic backup\n"
	}, false)

	cmd := c.Init()
	// The preview func is called asynchronously via a tea.Cmd.
	// Execute the returned cmd to get the message.
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	if !called {
		t.Error("Init() did not call the preview function")
	}
}

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

	preview := "environ:\n  RESTIC_REPOSITORY=/repo\ncommand: \"restic\" \"backup\" \"/src\"\n"
	c := NewConfirm(func() string { return preview }, false)

	cmd := c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	view := c.View()
	if !strings.Contains(view, "RESTIC_REPOSITORY") {
		t.Errorf("View() missing environ; got:\n%s", view)
	}
	if !strings.Contains(view, "restic") {
		t.Errorf("View() missing command; got:\n%s", view)
	}
}

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	cmd := c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	_, cmd = c.Update(tea.KeyPressMsg{Code: tea.KeyEscape})

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

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	cmd := c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	_, cmd = c.Update(tea.KeyPressMsg{Code: tea.KeyEnter})

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

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	cmd := c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	c.Update(tea.KeyPressMsg{Code: tea.KeyEnter})

	if got := c.Selection(); got != "confirmed" {
		t.Errorf("Selection() after enter = %q, want %q", got, "confirmed")
	}
}

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

	// When --show-command is active, Init should return DoneCmd
	// immediately so the session completes without user interaction.
	c := NewConfirm(func() string { return "command: restic backup\n" }, true)
	cmd := c.Init()

	if cmd == nil {
		t.Fatal("expected DoneCmd from Init when showCommand is true")
	}
	msg := cmd()
	if _, ok := msg.(ui.DoneMsg); !ok {
		t.Errorf("Init() with showCommand produced %T, want ui.DoneMsg", msg)
	}
}

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

	called := false
	c := NewConfirm(func() string {
		called = true
		return "command: restic backup\n"
	}, true)

	c.Init()

	if called {
		t.Error("preview function should not be called when showCommand is true")
	}
}

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	if got := c.View(); got != "" {
		t.Errorf("View() before Init = %q, want empty", got)
	}
}

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	c.SetPartial(true)

	cmd := c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	view := c.View()
	if !strings.Contains(view, "may be incomplete") {
		t.Errorf("partial View() missing incomplete note; got:\n%s", view)
	}
	if !strings.Contains(view, "--show-command") {
		t.Errorf("partial View() missing --show-command hint; got:\n%s", view)
	}
}

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)

	cmd := c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	view := c.View()
	if strings.Contains(view, "may be incomplete") {
		t.Errorf("non-partial View() should not contain incomplete note; got:\n%s", view)
	}
}

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

	c := NewConfirm(func() string { return "command: restic backup\n" }, false)
	cmd := c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	// Confirm.
	c.Update(tea.KeyPressMsg{Code: tea.KeyEnter})

	// Re-init (simulates back navigation and return).
	cmd = c.Init()
	if cmd != nil {
		msg := cmd()
		c.Update(msg)
	}

	// Should be functional again — selection should be cleared.
	if got := c.Selection(); got != "" {
		t.Errorf("Selection() after re-init = %q, want empty", got)
	}

	// Should still respond to Enter.
	_, cmd = c.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
	if cmd == nil {
		t.Fatal("expected DoneCmd after re-init and Enter")
	}
	msg := cmd()
	if _, ok := msg.(ui.DoneMsg); !ok {
		t.Errorf("after re-init, cmd produced %T, want ui.DoneMsg", msg)
	}
}
