package editor

import (
	"testing"
	"time"

	"git.gophernest.net/azpect/TextEditor/internal/action"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/x/exp/teatest"
)

// sendKeys sends a sequence of keys to the test model
func sendKeys(tm *teatest.TestModel, keys ...string) {
	for _, key := range keys {
		switch key {
		case "esc":
			tm.Send(tea.KeyMsg{Type: tea.KeyEscape})
		case "enter":
			tm.Send(tea.KeyMsg{Type: tea.KeyEnter})
		case "backspace":
			tm.Send(tea.KeyMsg{Type: tea.KeyBackspace})
		case "ctrl+c":
			tm.Send(tea.KeyMsg{Type: tea.KeyCtrlC})
		case "ctrl+d":
			tm.Send(tea.KeyMsg{Type: tea.KeyCtrlD})
		case "ctrl+u":
			tm.Send(tea.KeyMsg{Type: tea.KeyCtrlU})
		case "ctrl+v":
			tm.Send(tea.KeyMsg{Type: tea.KeyCtrlV})
		case "ctrl+w":
			tm.Send(tea.KeyMsg{Type: tea.KeyCtrlW})
		default:
			tm.Send(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(key)})
		}
	}
}

// TestModelOption is a functional option for configuring test models
type TestModelOption func(*testModelConfig)

type testModelConfig struct {
	lines      []string
	pos        action.Position
	width      int
	height     int
	regName    rune
	regType    action.RegisterType
	regContent []string
}

// WithLines sets the initial buffer lines
func WithLines(lines []string) TestModelOption {
	return func(c *testModelConfig) {
		c.lines = lines
	}
}

// WithCursorPos sets the initial cursor position
func WithCursorPos(pos action.Position) TestModelOption {
	return func(c *testModelConfig) {
		c.pos = pos
	}
}

// WithTermSize sets the terminal dimensions
func WithTermSize(width, height int) TestModelOption {
	return func(c *testModelConfig) {
		c.width = width
		c.height = height
	}
}

// WithRegister sets a register's content
func WithRegister(name rune, regType action.RegisterType, content []string) TestModelOption {
	return func(c *testModelConfig) {
		c.regName = name
		c.regType = regType
		c.regContent = content
	}
}

// newTestModel creates a test model with optional configuration
func newTestModel(t *testing.T, opts ...TestModelOption) *teatest.TestModel {
	// Default configuration
	cfg := testModelConfig{
		lines:  []string{"line 1", "line 2", "line 3", "line 4", "line 5", "line 6"},
		pos:    action.Position{Col: 0, Line: 0},
		width:  80,
		height: 24,
	}

	// Apply options
	for _, opt := range opts {
		opt(&cfg)
	}

	// Create model
	m := NewModel(cfg.lines, cfg.pos)

	// Set register if provided
	if cfg.regContent != nil {
		m.SetRegister(cfg.regName, cfg.regType, cfg.regContent)
	}

	return teatest.NewTestModel(t, m, teatest.WithInitialTermSize(cfg.width, cfg.height))
}

// Convenience functions for backward compatibility
func newTestModelWithLines(t *testing.T, lines []string) *teatest.TestModel {
	return newTestModel(t, WithLines(lines))
}

func newTestModelWithCursorPos(t *testing.T, pos action.Position) *teatest.TestModel {
	return newTestModel(t, WithCursorPos(pos))
}

func newTestModelWithLinesAndCursorPos(t *testing.T, lines []string, pos action.Position) *teatest.TestModel {
	return newTestModel(t, WithLines(lines), WithCursorPos(pos))
}

func newTestModelWithTermSize(t *testing.T, lines []string, pos action.Position, width, height int) *teatest.TestModel {
	return newTestModel(t, WithLines(lines), WithCursorPos(pos), WithTermSize(width, height))
}

// getFinalModel extracts the final model state (sends ctrl+c to quit first)
func getFinalModel(t *testing.T, tm *teatest.TestModel) *Model {
	tm.Send(tea.KeyMsg{Type: tea.KeyCtrlC})
	fm := tm.FinalModel(t, teatest.WithFinalTimeout(time.Second))
	return fm.(*Model)
}
