package action

import (
	"fmt"
	"strings"

	tea "github.com/charmbracelet/bubbletea"
)

// Paste implements Action (p) - pastes after cursor
type Paste struct {
	Count int
}

func (a Paste) Execute(m Model) tea.Cmd {
	// Get reg
	reg, found := m.GetRegister('"')
	if !found {
		m.SetCommandError(fmt.Errorf("\"\" register is broken. Uh oh."))
		return nil
	}

	// Exit if blank
	if len(reg.Content) == 0 {
		return nil
	}

	switch reg.Type {
	case LinewiseRegister:
		{
			initY := m.CursorY()
			lines := reg.Content
			insertPos := initY + 1

			// Run count times
			for range a.Count {
				for _, line := range lines {
					m.InsertLine(insertPos, line)
					insertPos++
				}
			}

			if m.LineCount() > 1 {
				m.SetCursorY(initY + 1)
			}
		}
	case CharwiseRegister:
		{
			lines := reg.Content

			// Shouldn't happen, just a check
			if len(lines) != 1 {
				m.SetCommandError(fmt.Errorf("Charwise register should only have a single line of content."))
				break
			}

			x := m.CursorX()
			y := m.CursorY()

			cnt := strings.Repeat(lines[0], max(1, a.Count))
			curLine := m.Line(y)

			// Catch edge cases, end of line, start of blank line
			insertAt := min(x+1, len(curLine))
			newLine := curLine[:insertAt] + cnt + curLine[insertAt:]
			m.SetLine(y, newLine)

			m.SetCursorX(x + len(cnt))
			m.ClampCursorX()
		}
	default:
		m.SetCommandError(fmt.Errorf("Register type is not implemented."))
	}

	return nil
}

// Ensure Paste implements Repeatable
var _ Repeatable = Paste{}

func (a Paste) WithCount(n int) Action {
	return Paste{Count: n}
}

// PasteBefore implements Action (P) - pastes before cursor
type PasteBefore struct {
	Count int
}

func (a PasteBefore) Execute(m Model) tea.Cmd {
	// Get reg
	reg, found := m.GetRegister('"')
	if !found {
		m.SetCommandError(fmt.Errorf("\"\" register is broken. Uh oh."))
		return nil
	}

	switch reg.Type {
	case LinewiseRegister:
		{
			initY := m.CursorY()
			lines := reg.Content
			insertPos := initY // Leave here, this will effectively move the lines below

			// Run count times
			for range a.Count {
				for _, line := range lines {
					m.InsertLine(insertPos, line)
					insertPos++
				}
			}
		}
	case CharwiseRegister:
		{
			lines := reg.Content

			// Shouldn't happen, just a check
			if len(lines) != 1 {
				m.SetCommandError(fmt.Errorf("Charwise register should only have a single line of content."))
				break
			}

			x := m.CursorX()
			y := m.CursorY()

			cnt := strings.Repeat(lines[0], max(1, a.Count))
			curLine := m.Line(y)

			// Catch edge cases, end of line, start of blank line
			insertAt := min(x, len(curLine))
			newLine := curLine[:insertAt] + cnt + curLine[insertAt:]
			m.SetLine(y, newLine)

			m.SetCursorX(x + len(cnt))
			m.ClampCursorX()
		}
	default:
		m.SetCommandError(fmt.Errorf("Register type is not implemented."))
	}

	return nil
}

// Ensure PasteBefore implements Repeatable
var _ Repeatable = PasteBefore{}

func (a PasteBefore) WithCount(n int) Action {
	return PasteBefore{Count: n}
}

// VisualPaste implements Action (p in visual mode) - replaces selection with register content
type VisualPaste struct {
	Count int
}

func (a VisualPaste) Execute(m Model) tea.Cmd {
	// Get register content to paste
	reg, found := m.GetRegister('"')
	if !found {
		m.SetCommandError(fmt.Errorf("\"\" register is broken. Uh oh."))
		return nil
	}

	mode := m.Mode()

	// Get selection bounds (normalize anchor and cursor)
	start, end := normalizeSelection(m)

	switch mode {
	case VisualMode:
		visualCharPaste(m, reg, start, end)
	case VisualBlockMode:
		visualBlockPaste(m, reg, start, end)
	case VisualLineMode:
		visualLinePaste(m, reg, start, end)
	}

	// Exit visual mode
	m.SetMode(NormalMode)

	return nil
}

// normalizeSelection returns start and end positions with start always before end
func normalizeSelection(m Model) (Position, Position) {
	anchorX, anchorY := m.AnchorX(), m.AnchorY()
	cursorX, cursorY := m.CursorX(), m.CursorY()

	start := Position{Line: anchorY, Col: anchorX}
	end := Position{Line: cursorY, Col: cursorX}

	// Normalize so start is always before end
	if start.Line > end.Line || (start.Line == end.Line && start.Col > end.Col) {
		start, end = end, start
	}

	return start, end
}

// visualCharPaste handles paste in visual (character) mode
func visualCharPaste(m Model, reg Register, start, end Position) {
	// First, extract the text that will be deleted (to save to register)
	deletedText := extractCharSelection(m, start, end)

	// Delete the selection
	deleteCharSelectionForPaste(m, start, end)

	// Insert the register content at start position
	if len(reg.Content) == 0 {
		// Empty register - just delete (already done)
	} else if reg.Type == CharwiseRegister {
		// Charwise paste: insert text at cursor position
		if len(reg.Content) == 1 {
			line := m.Line(start.Line)
			insertAt := min(start.Col, len(line))
			newLine := line[:insertAt] + reg.Content[0] + line[insertAt:]
			m.SetLine(start.Line, newLine)

			// Cursor at end of pasted text
			m.SetCursorX(insertAt + len(reg.Content[0]) - 1)
			m.SetCursorY(start.Line)
		}
	} else if reg.Type == LinewiseRegister {
		// Linewise paste in visual char mode: replace selection with lines
		// Insert each line from register
		for i, content := range reg.Content {
			if i == 0 {
				// First line: insert at start position
				line := m.Line(start.Line)
				insertAt := min(start.Col, len(line))
				newLine := line[:insertAt] + content
				if len(reg.Content) == 1 {
					// Single line register - append rest of line
					newLine += line[insertAt:]
				}
				m.SetLine(start.Line, newLine)
			} else {
				// Subsequent lines: insert new lines
				m.InsertLine(start.Line+i, content)
			}
		}
		m.SetCursorY(start.Line)
		m.SetCursorX(start.Col)
	}

	m.ClampCursorX()

	// Update register with deleted text
	m.UpdateDefaultRegister(CharwiseRegister, []string{deletedText})
}

// visualBlockPaste handles paste in visual block mode
func visualBlockPaste(m Model, reg Register, start, end Position) {
	startCol := min(start.Col, end.Col)
	endCol := max(start.Col, end.Col)

	// Extract deleted text (for register)
	var deletedLines []string
	for y := start.Line; y <= end.Line; y++ {
		line := m.Line(y)
		if startCol < len(line) {
			ec := min(endCol+1, len(line))
			deletedLines = append(deletedLines, line[startCol:ec])
		} else {
			deletedLines = append(deletedLines, "")
		}
	}

	// Delete the block selection
	for y := start.Line; y <= end.Line; y++ {
		line := m.Line(y)
		if startCol >= len(line) {
			continue
		}
		ec := min(endCol+1, len(line))
		m.SetLine(y, line[:startCol]+line[ec:])
	}

	// Insert register content
	if len(reg.Content) > 0 {
		pasteContent := reg.Content[0]
		if reg.Type == LinewiseRegister && len(reg.Content) > 0 {
			pasteContent = reg.Content[0]
		}

		for y := start.Line; y <= end.Line; y++ {
			line := m.Line(y)
			insertAt := min(startCol, len(line))
			// Pad with spaces if needed
			for len(line) < insertAt {
				line += " "
			}
			newLine := line[:insertAt] + pasteContent + line[insertAt:]
			m.SetLine(y, newLine)
		}
	}

	m.SetCursorY(start.Line)
	m.SetCursorX(startCol)
	m.ClampCursorX()

	// Update register with deleted block text (joined)
	m.UpdateDefaultRegister(CharwiseRegister, []string{strings.Join(deletedLines, "\n")})
}

// visualLinePaste handles paste in visual line mode
func visualLinePaste(m Model, reg Register, start, end Position) {
	// Extract deleted lines (for register)
	var deletedLines []string
	for y := start.Line; y <= end.Line; y++ {
		deletedLines = append(deletedLines, m.Line(y))
	}

	// Delete the selected lines (from end to start to preserve indices)
	for y := end.Line; y >= start.Line; y-- {
		m.DeleteLine(y)
	}

	// Insert register content
	if len(reg.Content) == 0 {
		// Empty register - ensure at least one empty line exists
		if m.LineCount() == 0 {
			m.InsertLine(0, "")
		}
	} else if reg.Type == LinewiseRegister {
		// Linewise register: insert each line
		insertPos := start.Line
		for _, content := range reg.Content {
			m.InsertLine(insertPos, content)
			insertPos++
		}
	} else {
		// Charwise register: insert as a single line
		m.InsertLine(start.Line, reg.Content[0])
	}

	// Ensure we have at least one line
	if m.LineCount() == 0 {
		m.InsertLine(0, "")
	}

	// Position cursor at start of pasted content
	y := start.Line
	if y >= m.LineCount() {
		y = m.LineCount() - 1
	}
	m.SetCursorY(y)
	m.SetCursorX(0)
	m.ClampCursorX()

	// Update register with deleted lines
	m.UpdateDefaultRegister(LinewiseRegister, deletedLines)
}

// extractCharSelection extracts text from a character selection
func extractCharSelection(m Model, start, end Position) string {
	if start.Line == end.Line {
		line := m.Line(start.Line)
		endCol := min(end.Col+1, len(line))
		startCol := min(start.Col, len(line))
		if startCol >= endCol {
			return ""
		}
		return line[startCol:endCol]
	}

	// Multi-line selection
	var result strings.Builder

	// First line: from start.Col to end
	firstLine := m.Line(start.Line)
	if start.Col < len(firstLine) {
		result.WriteString(firstLine[start.Col:])
	}
	result.WriteString("\n")

	// Middle lines: entire lines
	for y := start.Line + 1; y < end.Line; y++ {
		result.WriteString(m.Line(y))
		result.WriteString("\n")
	}

	// Last line: from beginning to end.Col
	lastLine := m.Line(end.Line)
	endCol := min(end.Col+1, len(lastLine))
	result.WriteString(lastLine[:endCol])

	return result.String()
}

// deleteCharSelectionForPaste deletes a character selection (similar to operator/delete.go)
func deleteCharSelectionForPaste(m Model, start, end Position) {
	if start.Line == end.Line {
		line := m.Line(start.Line)
		endCol := min(end.Col+1, len(line))
		m.SetLine(start.Line, line[:start.Col]+line[endCol:])
	} else {
		startLine := m.Line(start.Line)
		endLine := m.Line(end.Line)

		prefix := ""
		if start.Col < len(startLine) {
			prefix = startLine[:start.Col]
		}

		suffix := ""
		if end.Col+1 < len(endLine) {
			suffix = endLine[end.Col+1:]
		}

		// Delete from end back to start to preserve indices
		for i := end.Line; i >= start.Line; i-- {
			m.DeleteLine(i)
		}
		m.InsertLine(start.Line, prefix+suffix)
	}

	m.SetCursorY(start.Line)
	m.SetCursorX(start.Col)
}

// Ensure VisualPaste implements Repeatable
var _ Repeatable = VisualPaste{}

func (a VisualPaste) WithCount(n int) Action {
	return VisualPaste{Count: n}
}
