package editor

import (
	"testing"

	"git.gophernest.net/azpect/TextEditor/internal/action"
)

// NOTE: AI Generated tests

// Default settings are: Number=true, RelativeNumber=true, TabSize=2, ScrollOff=8

func TestCommandSetBoolean(t *testing.T) {
	t.Run("':set nonumber' disables line numbers", func(t *testing.T) {
		// Default has Number=true
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		sendKeys(tm, ":", "s", "e", "t", " ", "n", "o", "n", "u", "m", "b", "e", "r", "enter")

		m := getFinalModel(t, tm)
		if m.Settings().Number {
			t.Error("expected Number to be false after :set nonumber")
		}
	})

	t.Run("':set number' enables after disable", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		// First disable, then enable
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "o", "n", "u", "enter")
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "enter")

		m := getFinalModel(t, tm)
		if !m.Settings().Number {
			t.Error("expected Number to be true after :set nu")
		}
	})

	t.Run("':set number!' toggles number off", func(t *testing.T) {
		// Default has Number=true
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "m", "b", "e", "r", "!", "enter")

		m := getFinalModel(t, tm)
		if m.Settings().Number {
			t.Error("expected Number to be false after :set number!")
		}
	})

	t.Run("':set number!' toggles back on", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		// Toggle off then on
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "!", "enter")
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "!", "enter")

		m := getFinalModel(t, tm)
		if !m.Settings().Number {
			t.Error("expected Number to be true after double toggle")
		}
	})

	t.Run("':set nornu' disables relative numbers", func(t *testing.T) {
		// Default has RelativeNumber=true
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		sendKeys(tm, ":", "s", "e", "t", " ", "n", "o", "r", "n", "u", "enter")

		m := getFinalModel(t, tm)
		if m.Settings().RelativeNumber {
			t.Error("expected RelativeNumber to be false after :set nornu")
		}
	})

	t.Run("':set rnu' enables after disable", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		// Disable then enable
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "o", "r", "n", "u", "enter")
		sendKeys(tm, ":", "s", "e", "t", " ", "r", "n", "u", "enter")

		m := getFinalModel(t, tm)
		if !m.Settings().RelativeNumber {
			t.Error("expected RelativeNumber to be true after :set rnu")
		}
	})
}

func TestCommandSetInteger(t *testing.T) {
	t.Run("':set tabstop=4' sets tab size", func(t *testing.T) {
		// Default TabSize=2
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		sendKeys(tm, ":", "s", "e", "t", " ", "t", "a", "b", "s", "t", "o", "p", "=", "4", "enter")

		m := getFinalModel(t, tm)
		if m.Settings().TabSize != 4 {
			t.Errorf("TabSize = %d, want 4", m.Settings().TabSize)
		}
	})

	t.Run("':set ts=8' uses short form", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		sendKeys(tm, ":", "s", "e", "t", " ", "t", "s", "=", "8", "enter")

		m := getFinalModel(t, tm)
		if m.Settings().TabSize != 8 {
			t.Errorf("TabSize = %d, want 8", m.Settings().TabSize)
		}
	})

	t.Run("':set scrolloff=5' sets scroll offset", func(t *testing.T) {
		// Default ScrollOff=8
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		sendKeys(tm, ":", "s", "e", "t", " ", "s", "c", "r", "o", "l", "l", "o", "f", "f", "=", "5", "enter")

		m := getFinalModel(t, tm)
		if m.Settings().ScrollOff != 5 {
			t.Errorf("ScrollOff = %d, want 5", m.Settings().ScrollOff)
		}
	})

	t.Run("':set so=10' uses short form", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		sendKeys(tm, ":", "s", "e", "t", " ", "s", "o", "=", "1", "0", "enter")

		m := getFinalModel(t, tm)
		if m.Settings().ScrollOff != 10 {
			t.Errorf("ScrollOff = %d, want 10", m.Settings().ScrollOff)
		}
	})
}

func TestCommandModeNavigation(t *testing.T) {
	t.Run("':' enters command mode and esc exits", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "esc")

		m := getFinalModel(t, tm)
		// After esc we should be back in normal mode
		if m.Mode() != action.NormalMode {
			t.Errorf("Mode() = %v, want NormalMode after esc", m.Mode())
		}
	})

	t.Run("'esc' exits command mode", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "esc")

		m := getFinalModel(t, tm)
		if m.Mode() != action.NormalMode {
			t.Errorf("Mode() = %v, want NormalMode", m.Mode())
		}
	})

	t.Run("'enter' executes and exits command mode", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "enter")

		m := getFinalModel(t, tm)
		if m.Mode() != action.NormalMode {
			t.Errorf("Mode() = %v, want NormalMode", m.Mode())
		}
	})

	t.Run("command buffer is not cleared after execution", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "t", "e", "s", "t", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "test" {
			t.Errorf("Command() = %q, want \"test\"", m.Command())
		}
	})
}

func TestCommandModeEditing(t *testing.T) {
	t.Run("backspace deletes character", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		// Type abc, backspace, then enter to execute and check result
		sendKeys(tm, ":", "a", "b", "c", "backspace", "enter")

		m := getFinalModel(t, tm)
		// Command buffer preserves last command after execution
		if m.Command() != "ab" {
			t.Errorf("Command() = %q, want \"ab\"", m.Command())
		}
	})

	t.Run("backspace at start does nothing", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "backspace", "a", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "a" {
			t.Errorf("Command() = %q, want \"a\"", m.Command())
		}
	})

	t.Run("multiple backspaces", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "a", "b", "c", "backspace", "backspace", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "a" {
			t.Errorf("Command() = %q, want \"a\"", m.Command())
		}
	})

	t.Run("delete key removes character at cursor", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		// Type abc, move left, delete 'c', then enter
		sendKeys(tm, ":", "a", "b", "c", "left", "delete", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "ab" {
			t.Errorf("Command() = %q, want \"ab\"", m.Command())
		}
	})

	t.Run("delete at end acts as backspace", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "a", "b", "c", "delete", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "ab" {
			t.Errorf("Command() = %q, want \"ab\"", m.Command())
		}
	})

	t.Run("ctrl+w deletes previous word", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "m", "b", "e", "r", "ctrl+w", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "set " {
			t.Errorf("Command() = %q, want \"set \"", m.Command())
		}
	})

	t.Run("ctrl+w deletes word and trailing space", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "o", "n", "e", " ", "t", "w", "o", " ", "ctrl+w", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "one " {
			t.Errorf("Command() = %q, want \"one \"", m.Command())
		}
	})

	t.Run("ctrl+w at start does nothing", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "ctrl+w", "a", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "a" {
			t.Errorf("Command() = %q, want \"a\"", m.Command())
		}
	})

	t.Run("insert at cursor position", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		// Type 'ac', move left, insert 'b' -> 'abc'
		sendKeys(tm, ":", "a", "c", "left", "b", "enter")

		m := getFinalModel(t, tm)
		if m.Command() != "abc" {
			t.Errorf("Command() = %q, want \"abc\"", m.Command())
		}
	})
}

func TestCommandModeErrors(t *testing.T) {
	t.Run("unknown command sets error", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, ":", "u", "n", "k", "n", "o", "w", "n", "c", "m", "d", "enter")

		m := getFinalModel(t, tm)
		if m.commandError == nil {
			t.Error("expected commandError to be set for unknown command")
		}
	})

	t.Run("valid command clears error", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		// First cause an error
		sendKeys(tm, ":", "b", "a", "d", "enter")
		// Then run a valid command
		sendKeys(tm, ":", "s", "e", "t", " ", "n", "u", "enter")

		m := getFinalModel(t, tm)
		if m.commandError != nil {
			t.Errorf("expected commandError to be nil, got %v", m.commandError)
		}
	})

	t.Run("esc clears error", func(t *testing.T) {
		lines := []string{"hello"}
		tm := newTestModelWithLines(t, lines)

		// Cause an error
		sendKeys(tm, ":", "b", "a", "d", "enter")
		// Then escape
		sendKeys(tm, ":", "esc")

		m := getFinalModel(t, tm)
		if m.commandError != nil {
			t.Errorf("expected commandError to be nil after esc, got %v", m.commandError)
		}
	})
}
