package action

import (
	tea "github.com/charmbracelet/bubbletea"
)

type ExitCommandMode struct{}

func (a ExitCommandMode) Execute(m Model) tea.Cmd {
	m.SetCommandCursor(0)
	m.SetCommand("")
	m.SetCommandOutput("")
	m.SetCommandError(nil)
	m.SetMode(NormalMode)
	return nil
}

type InsertCommandChar struct {
	Char string
}

func (a InsertCommandChar) Execute(m Model) tea.Cmd {
	cur := m.CommandCursor()
	cmd := m.Command()

	m.SetCommand(cmd[:cur] + a.Char + cmd[cur:])
	m.SetCommandCursor(cur + 1)
	return nil
}

type CommandBackspace struct{}

func (a CommandBackspace) Execute(m Model) tea.Cmd {
	cur := m.CommandCursor()
	cmd := m.Command()

	if cur > 0 {
		m.SetCommand(cmd[:cur-1] + cmd[cur:])
		m.SetCommandCursor(cur - 1)
	}

	return nil
}

type CommandDelete struct{}

func (a CommandDelete) Execute(m Model) tea.Cmd {
	cur := m.CommandCursor()
	cmd := m.Command()

	if cur < len(cmd)-1 {
		m.SetCommand(cmd[:cur+1] + cmd[cur+2:])
	} else if cur == len(cmd)-1 {
		// last text char, delete it
		m.SetCommand(cmd[:cur] + cmd[cur+1:])
	} else if cur == len(cmd) && cur > 0 {
		// if at end, we do backspace op
		m.SetCommand(cmd[:cur-1] + cmd[cur:])
		m.SetCommandCursor(cur - 1)
	}

	return nil
}

type CommandDeletePreviousWord struct{}

func (a CommandDeletePreviousWord) Execute(m Model) tea.Cmd {
	cur := m.CommandCursor()
	cmd := m.Command()

	if cur > 0 {
		newCur := cur

		// If we are on puncuation, we should just skip them all and quit
		if isPunctuation(cmd[newCur-1]) {
			for newCur > 0 && isPunctuation(cmd[newCur-1]) {
				newCur--
			}

			m.SetCommand(cmd[:newCur] + cmd[cur:])
			m.SetCommandCursor(newCur)

			return nil
		}

		// Skip whitespace immediately before the cursor
		for newCur > 0 && (cmd[newCur-1] == ' ' || cmd[newCur-1] == '\t') {
			newCur--
		}

		// Skip the word characters before the cursor
		for newCur > 0 && isWordChar(cmd[newCur-1]) {
			newCur--
		}

		// Delete everything from newCur up to cur in one operation
		m.SetCommand(cmd[:newCur] + cmd[cur:])
		m.SetCommandCursor(newCur)
	}

	return nil
}

type CommandExecute struct {
	Registry CommandRegistry
}

// CommandRegistry interface for executing commands
type CommandRegistry interface {
	Execute(m Model, cmdLine string) (tea.Cmd, error)
}

func (a CommandExecute) Execute(m Model) tea.Cmd {
	cmdLine := m.Command()

	// Clear command state and return to normal mode
	m.SetCommandCursor(0)
	m.SetCommandError(nil)
	m.SetMode(NormalMode)

	if a.Registry == nil || cmdLine == "" {
		return nil
	}

	cmd, err := a.Registry.Execute(m, cmdLine)
	if err != nil {
		// TODO: Display error message to user
		m.SetCommandError(err)
		return nil
	}

	return cmd
}
