package editor

import (
	"testing"

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

func TestPasteLinewiseBasic(t *testing.T) {
	t.Run("p pastes single line after cursor line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 3 {
			t.Errorf("LineCount() = %d, want 3", m.LineCount())
		}
		if m.Line(0) != "line 1" {
			t.Errorf("Line(0) = %q, want 'line 1'", m.Line(0))
		}
		if m.Line(1) != "inserted" {
			t.Errorf("Line(1) = %q, want 'inserted'", m.Line(1))
		}
		if m.Line(2) != "line 2" {
			t.Errorf("Line(2) = %q, want 'line 2'", m.Line(2))
		}
	})

	t.Run("p moves cursor to first pasted line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2", "line 3"}),
			WithCursorPos(action.Position{Line: 0, Col: 5}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.CursorY() != 1 {
			t.Errorf("CursorY() = %d, want 1", m.CursorY())
		}
	})

	t.Run("p from middle of file", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2", "line 3"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 4 {
			t.Errorf("LineCount() = %d, want 4", m.LineCount())
		}
		if m.Line(2) != "inserted" {
			t.Errorf("Line(2) = %q, want 'inserted'", m.Line(2))
		}
		if m.CursorY() != 2 {
			t.Errorf("CursorY() = %d, want 2", m.CursorY())
		}
	})

	t.Run("p at end of file", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 3 {
			t.Errorf("LineCount() = %d, want 3", m.LineCount())
		}
		if m.Line(2) != "inserted" {
			t.Errorf("Line(2) = %q, want 'inserted'", m.Line(2))
		}
	})
}

func TestPasteLinewiseMultipleLines(t *testing.T) {
	t.Run("p pastes multiple lines in correct order", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"first", "second", "third"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 5 {
			t.Errorf("LineCount() = %d, want 5", m.LineCount())
		}
		if m.Line(0) != "line 1" {
			t.Errorf("Line(0) = %q, want 'line 1'", m.Line(0))
		}
		if m.Line(1) != "first" {
			t.Errorf("Line(1) = %q, want 'first'", m.Line(1))
		}
		if m.Line(2) != "second" {
			t.Errorf("Line(2) = %q, want 'second'", m.Line(2))
		}
		if m.Line(3) != "third" {
			t.Errorf("Line(3) = %q, want 'third'", m.Line(3))
		}
		if m.Line(4) != "line 2" {
			t.Errorf("Line(4) = %q, want 'line 2'", m.Line(4))
		}
	})

	t.Run("p with multiple lines moves cursor to first pasted line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"first", "second"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.CursorY() != 1 {
			t.Errorf("CursorY() = %d, want 1", m.CursorY())
		}
	})
}

func TestPasteLinewiseWithCount(t *testing.T) {
	t.Run("2p pastes content twice", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "2", "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 4 {
			t.Errorf("LineCount() = %d, want 4", m.LineCount())
		}
		// Both "inserted" lines should appear after line 1
		if m.Line(1) != "inserted" {
			t.Errorf("Line(1) = %q, want 'inserted'", m.Line(1))
		}
		if m.Line(2) != "inserted" {
			t.Errorf("Line(2) = %q, want 'inserted'", m.Line(2))
		}
		if m.Line(3) != "line 2" {
			t.Errorf("Line(3) = %q, want 'line 2'", m.Line(3))
		}
	})

	t.Run("3p pastes content three times", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"original"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"pasted"}),
		)
		sendKeys(tm, "3", "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 4 {
			t.Errorf("LineCount() = %d, want 4", m.LineCount())
		}
		for i := 1; i <= 3; i++ {
			if m.Line(i) != "pasted" {
				t.Errorf("Line(%d) = %q, want 'pasted'", i, m.Line(i))
			}
		}
	})

	t.Run("2p with multiple lines pastes all lines twice in order", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"original"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"first", "second"}),
		)
		sendKeys(tm, "2", "p")

		m := getFinalModel(t, tm)
		// Should be: original, first, second, first, second
		if m.LineCount() != 5 {
			t.Errorf("LineCount() = %d, want 5", m.LineCount())
		}
		if m.Line(0) != "original" {
			t.Errorf("Line(0) = %q, want 'original'", m.Line(0))
		}
		if m.Line(1) != "first" {
			t.Errorf("Line(1) = %q, want 'first'", m.Line(1))
		}
		if m.Line(2) != "second" {
			t.Errorf("Line(2) = %q, want 'second'", m.Line(2))
		}
		if m.Line(3) != "first" {
			t.Errorf("Line(3) = %q, want 'first'", m.Line(3))
		}
		if m.Line(4) != "second" {
			t.Errorf("Line(4) = %q, want 'second'", m.Line(4))
		}
	})

	t.Run("count paste moves cursor to first pasted line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "3", "p")

		m := getFinalModel(t, tm)
		if m.CursorY() != 1 {
			t.Errorf("CursorY() = %d, want 1", m.CursorY())
		}
	})
}

// Tests for P (paste before)

func TestPasteBeforeLinewiseBasic(t *testing.T) {
	t.Run("P pastes single line before cursor line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 3 {
			t.Errorf("LineCount() = %d, want 3", m.LineCount())
		}
		if m.Line(0) != "line 1" {
			t.Errorf("Line(0) = %q, want 'line 1'", m.Line(0))
		}
		if m.Line(1) != "inserted" {
			t.Errorf("Line(1) = %q, want 'inserted'", m.Line(1))
		}
		if m.Line(2) != "line 2" {
			t.Errorf("Line(2) = %q, want 'line 2'", m.Line(2))
		}
	})

	t.Run("P moves cursor to first pasted line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2", "line 3"}),
			WithCursorPos(action.Position{Line: 1, Col: 5}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.CursorY() != 1 {
			t.Errorf("CursorY() = %d, want 1", m.CursorY())
		}
	})

	t.Run("P at first line pastes at very top", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 3 {
			t.Errorf("LineCount() = %d, want 3", m.LineCount())
		}
		if m.Line(0) != "inserted" {
			t.Errorf("Line(0) = %q, want 'inserted'", m.Line(0))
		}
		if m.Line(1) != "line 1" {
			t.Errorf("Line(1) = %q, want 'line 1'", m.Line(1))
		}
		if m.CursorY() != 0 {
			t.Errorf("CursorY() = %d, want 0", m.CursorY())
		}
	})

	t.Run("P from middle of file", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2", "line 3"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 4 {
			t.Errorf("LineCount() = %d, want 4", m.LineCount())
		}
		if m.Line(1) != "inserted" {
			t.Errorf("Line(1) = %q, want 'inserted'", m.Line(1))
		}
		if m.Line(2) != "line 2" {
			t.Errorf("Line(2) = %q, want 'line 2'", m.Line(2))
		}
	})
}

func TestPasteBeforeLinewiseMultipleLines(t *testing.T) {
	t.Run("P pastes multiple lines in correct order", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"first", "second", "third"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 5 {
			t.Errorf("LineCount() = %d, want 5", m.LineCount())
		}
		if m.Line(0) != "line 1" {
			t.Errorf("Line(0) = %q, want 'line 1'", m.Line(0))
		}
		if m.Line(1) != "first" {
			t.Errorf("Line(1) = %q, want 'first'", m.Line(1))
		}
		if m.Line(2) != "second" {
			t.Errorf("Line(2) = %q, want 'second'", m.Line(2))
		}
		if m.Line(3) != "third" {
			t.Errorf("Line(3) = %q, want 'third'", m.Line(3))
		}
		if m.Line(4) != "line 2" {
			t.Errorf("Line(4) = %q, want 'line 2'", m.Line(4))
		}
	})

	t.Run("P with multiple lines moves cursor to first pasted line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"first", "second"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.CursorY() != 1 {
			t.Errorf("CursorY() = %d, want 1", m.CursorY())
		}
	})
}

func TestPasteBeforeLinewiseWithCount(t *testing.T) {
	t.Run("2P pastes content twice", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "2", "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 4 {
			t.Errorf("LineCount() = %d, want 4", m.LineCount())
		}
		// Both "inserted" lines should appear before line 2
		if m.Line(1) != "inserted" {
			t.Errorf("Line(1) = %q, want 'inserted'", m.Line(1))
		}
		if m.Line(2) != "inserted" {
			t.Errorf("Line(2) = %q, want 'inserted'", m.Line(2))
		}
		if m.Line(3) != "line 2" {
			t.Errorf("Line(3) = %q, want 'line 2'", m.Line(3))
		}
	})

	t.Run("3P pastes content three times", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"original"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"pasted"}),
		)
		sendKeys(tm, "3", "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 4 {
			t.Errorf("LineCount() = %d, want 4", m.LineCount())
		}
		for i := 0; i < 3; i++ {
			if m.Line(i) != "pasted" {
				t.Errorf("Line(%d) = %q, want 'pasted'", i, m.Line(i))
			}
		}
		if m.Line(3) != "original" {
			t.Errorf("Line(3) = %q, want 'original'", m.Line(3))
		}
	})

	t.Run("2P with multiple lines pastes all lines twice in order", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"original"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"first", "second"}),
		)
		sendKeys(tm, "2", "P")

		m := getFinalModel(t, tm)
		// Should be: first, second, first, second, original
		if m.LineCount() != 5 {
			t.Errorf("LineCount() = %d, want 5", m.LineCount())
		}
		if m.Line(0) != "first" {
			t.Errorf("Line(0) = %q, want 'first'", m.Line(0))
		}
		if m.Line(1) != "second" {
			t.Errorf("Line(1) = %q, want 'second'", m.Line(1))
		}
		if m.Line(2) != "first" {
			t.Errorf("Line(2) = %q, want 'first'", m.Line(2))
		}
		if m.Line(3) != "second" {
			t.Errorf("Line(3) = %q, want 'second'", m.Line(3))
		}
		if m.Line(4) != "original" {
			t.Errorf("Line(4) = %q, want 'original'", m.Line(4))
		}
	})

	t.Run("count paste before moves cursor to first pasted line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "3", "P")

		m := getFinalModel(t, tm)
		if m.CursorY() != 1 {
			t.Errorf("CursorY() = %d, want 1", m.CursorY())
		}
	})
}

func TestPasteBeforeLinewiseEdgeCases(t *testing.T) {
	t.Run("P on single line buffer", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"only line"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
		if m.Line(0) != "inserted" {
			t.Errorf("Line(0) = %q, want 'inserted'", m.Line(0))
		}
		if m.Line(1) != "only line" {
			t.Errorf("Line(1) = %q, want 'only line'", m.Line(1))
		}
	})

	t.Run("P with empty register content does nothing", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
	})

	t.Run("P preserves indentation in pasted lines", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"    indented", "\ttabbed"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.Line(0) != "    indented" {
			t.Errorf("Line(0) = %q, want '    indented'", m.Line(0))
		}
		if m.Line(1) != "\ttabbed" {
			t.Errorf("Line(1) = %q, want '\\ttabbed'", m.Line(1))
		}
	})

	t.Run("P with large count", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"original"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"x"}),
		)
		sendKeys(tm, "1", "0", "P") // 10P

		m := getFinalModel(t, tm)
		if m.LineCount() != 11 {
			t.Errorf("LineCount() = %d, want 11", m.LineCount())
		}
		// Original should be at the end
		if m.Line(10) != "original" {
			t.Errorf("Line(10) = %q, want 'original'", m.Line(10))
		}
	})
}

func TestPasteLinewiseEdgeCases(t *testing.T) {
	t.Run("p on single line buffer", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"only line"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"inserted"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
		if m.Line(0) != "only line" {
			t.Errorf("Line(0) = %q, want 'only line'", m.Line(0))
		}
		if m.Line(1) != "inserted" {
			t.Errorf("Line(1) = %q, want 'inserted'", m.Line(1))
		}
	})

	t.Run("p with empty register content does nothing", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
	})

	t.Run("p preserves indentation in pasted lines", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"    indented", "\ttabbed"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(1) != "    indented" {
			t.Errorf("Line(1) = %q, want '    indented'", m.Line(1))
		}
		if m.Line(2) != "\ttabbed" {
			t.Errorf("Line(2) = %q, want '\\ttabbed'", m.Line(2))
		}
	})

	t.Run("p with large count", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"original"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"x"}),
		)
		sendKeys(tm, "1", "0", "p") // 10p

		m := getFinalModel(t, tm)
		if m.LineCount() != 11 {
			t.Errorf("LineCount() = %d, want 11", m.LineCount())
		}
	})
}

// =============================================================================
// Charwise Paste (p) Tests
// =============================================================================

func TestPasteCharwiseBasic(t *testing.T) {
	t.Run("p pastes text after cursor", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 4}), // on 'o'
			WithRegister('"', action.CharwiseRegister, []string{"XYZ"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		// Should insert after 'o': "helloXYZ world"
		if m.Line(0) != "helloXYZ world" {
			t.Errorf("Line(0) = %q, want 'helloXYZ world'", m.Line(0))
		}
	})

	t.Run("p at start of line pastes after first char", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "hXello" {
			t.Errorf("Line(0) = %q, want 'hXello'", m.Line(0))
		}
	})

	t.Run("p at end of line pastes at end", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 4}), // on 'o'
			WithRegister('"', action.CharwiseRegister, []string{"!"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello!" {
			t.Errorf("Line(0) = %q, want 'hello!'", m.Line(0))
		}
	})

	t.Run("p on empty line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{""}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"text"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "text" {
			t.Errorf("Line(0) = %q, want 'text'", m.Line(0))
		}
	})

	t.Run("p does not add new lines", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello", "world"}),
			WithCursorPos(action.Position{Line: 0, Col: 2}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
	})
}

func TestPasteCharwiseWithCount(t *testing.T) {
	t.Run("2p pastes content twice", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "2", "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "hXXello" {
			t.Errorf("Line(0) = %q, want 'hXXello'", m.Line(0))
		}
	})

	t.Run("3p pastes word three times", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"start end"}),
			WithCursorPos(action.Position{Line: 0, Col: 4}), // on 't'
			WithRegister('"', action.CharwiseRegister, []string{"-"}),
		)
		sendKeys(tm, "3", "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "start--- end" {
			t.Errorf("Line(0) = %q, want 'start--- end'", m.Line(0))
		}
	})

	t.Run("count paste with multi-char content", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"ab"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"XY"}),
		)
		sendKeys(tm, "2", "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "aXYXYb" {
			t.Errorf("Line(0) = %q, want 'aXYXYb'", m.Line(0))
		}
	})
}

func TestPasteCharwiseCursorPosition(t *testing.T) {
	t.Run("p moves cursor to end of pasted text", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"XYZ"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		// Cursor should be at position after pasted text
		if m.CursorX() != 3 {
			t.Errorf("CursorX() = %d, want 3", m.CursorX())
		}
	})

	t.Run("p cursor stays on same line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line 1", "line 2"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.CursorY() != 1 {
			t.Errorf("CursorY() = %d, want 1", m.CursorY())
		}
	})
}

// =============================================================================
// Charwise Paste Before (P) Tests
// =============================================================================

func TestPasteBeforeCharwiseBasic(t *testing.T) {
	t.Run("P pastes text before cursor", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 5}), // on space
			WithRegister('"', action.CharwiseRegister, []string{"XYZ"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		// Should insert before space: "helloXYZ world"
		if m.Line(0) != "helloXYZ world" {
			t.Errorf("Line(0) = %q, want 'helloXYZ world'", m.Line(0))
		}
	})

	t.Run("P at start of line pastes at beginning", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.Line(0) != "Xhello" {
			t.Errorf("Line(0) = %q, want 'Xhello'", m.Line(0))
		}
	})

	t.Run("P at end of line pastes before last char", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 4}), // on 'o'
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.Line(0) != "hellXo" {
			t.Errorf("Line(0) = %q, want 'hellXo'", m.Line(0))
		}
	})

	t.Run("P on empty line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{""}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"text"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.Line(0) != "text" {
			t.Errorf("Line(0) = %q, want 'text'", m.Line(0))
		}
	})
}

func TestPasteBeforeCharwiseWithCount(t *testing.T) {
	t.Run("2P pastes content twice", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 2}), // on first 'l'
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "2", "P")

		m := getFinalModel(t, tm)
		if m.Line(0) != "heXXllo" {
			t.Errorf("Line(0) = %q, want 'heXXllo'", m.Line(0))
		}
	})

	t.Run("3P pastes word three times", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"ab"}),
			WithCursorPos(action.Position{Line: 0, Col: 1}), // on 'b'
			WithRegister('"', action.CharwiseRegister, []string{"-"}),
		)
		sendKeys(tm, "3", "P")

		m := getFinalModel(t, tm)
		if m.Line(0) != "a---b" {
			t.Errorf("Line(0) = %q, want 'a---b'", m.Line(0))
		}
	})
}

// =============================================================================
// Multi-line Charwise Paste Tests (from visual mode yank)
// =============================================================================

func TestPasteCharwiseMultiLine(t *testing.T) {
	t.Run("p with multi-line charwise content errors gracefully", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"line1", "line2"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		// Current implementation errors - line should be unchanged
		if m.Line(0) != "hello" {
			t.Errorf("Line(0) = %q, want 'hello' (unchanged due to error)", m.Line(0))
		}
	})

	t.Run("P with multi-line charwise content errors gracefully", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"line1", "line2"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		// Current implementation errors - line should be unchanged
		if m.Line(0) != "hello" {
			t.Errorf("Line(0) = %q, want 'hello' (unchanged due to error)", m.Line(0))
		}
	})
}

// =============================================================================
// Blockwise Paste Tests
// =============================================================================

func TestPasteBlockwiseBasic(t *testing.T) {
	t.Run("p with blockwise content errors gracefully", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"aaaa", "bbbb", "cccc"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.BlockwiseRegister, []string{"XX", "YY", "ZZ"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		// Current implementation errors - lines should be unchanged
		if m.Line(0) != "aaaa" {
			t.Errorf("Line(0) = %q, want 'aaaa' (unchanged due to error)", m.Line(0))
		}
	})

	t.Run("P with blockwise content errors gracefully", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"aaaa", "bbbb", "cccc"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.BlockwiseRegister, []string{"XX", "YY", "ZZ"}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		// Current implementation errors - lines should be unchanged
		if m.Line(0) != "aaaa" {
			t.Errorf("Line(0) = %q, want 'aaaa' (unchanged due to error)", m.Line(0))
		}
	})
}

// =============================================================================
// Charwise Paste Edge Cases
// =============================================================================

func TestPasteCharwiseEdgeCases(t *testing.T) {
	t.Run("p with empty charwise register does nothing", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello" {
			t.Errorf("Line(0) = %q, want 'hello'", m.Line(0))
		}
	})

	t.Run("p with empty string in register", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{""}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello" {
			t.Errorf("Line(0) = %q, want 'hello'", m.Line(0))
		}
	})

	t.Run("p preserves special characters", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"ab"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"\t"}),
		)
		sendKeys(tm, "p")

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

	t.Run("p with spaces", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"ab"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"   "}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "a   b" {
			t.Errorf("Line(0) = %q, want 'a   b'", m.Line(0))
		}
	})

	t.Run("p on line with only whitespace", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"    "}),
			WithCursorPos(action.Position{Line: 0, Col: 2}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "   X " {
			t.Errorf("Line(0) = %q, want '   X '", m.Line(0))
		}
	})

	t.Run("P with empty charwise register does nothing", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{}),
		)
		sendKeys(tm, "P")

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello" {
			t.Errorf("Line(0) = %q, want 'hello'", m.Line(0))
		}
	})

	t.Run("large count paste", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"ab"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "1", "0", "p") // 10p

		m := getFinalModel(t, tm)
		if m.Line(0) != "aXXXXXXXXXXb" {
			t.Errorf("Line(0) = %q, want 'aXXXXXXXXXXb'", m.Line(0))
		}
	})
}

// =============================================================================
// Integration: Yank then Paste
// =============================================================================

func TestYankThenPasteCharwise(t *testing.T) {
	t.Run("yw then p pastes yanked word", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
		)
		sendKeys(tm, "y", "w")      // yank "hello "
		sendKeys(tm, "$")           // go to end
		sendKeys(tm, "p")           // paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello worldhello " {
			t.Errorf("Line(0) = %q, want 'hello worldhello '", m.Line(0))
		}
	})

	t.Run("ye then p pastes yanked word", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
		)
		sendKeys(tm, "y", "e")      // yank "hello"
		sendKeys(tm, "$")           // go to end
		sendKeys(tm, "p")           // paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello worldhello" {
			t.Errorf("Line(0) = %q, want 'hello worldhello'", m.Line(0))
		}
	})

	t.Run("visual select then y then p", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
		)
		sendKeys(tm, "v", "l", "l", "y") // select and yank "hel"
		sendKeys(tm, "$")                 // go to end
		sendKeys(tm, "p")                 // paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello worldhel" {
			t.Errorf("Line(0) = %q, want 'hello worldhel'", m.Line(0))
		}
	})
}

// =============================================================================
// Visual Mode Paste Tests
// =============================================================================
// In visual mode, 'p' should replace the selected text with register content.
// This is different from normal mode where 'p' inserts after cursor.

func TestVisualModePasteCharwise(t *testing.T) {
	// -------------------------------------------------------------------------
	// Basic Visual Mode Paste with Charwise Register
	// -------------------------------------------------------------------------

	t.Run("vp replaces selection with charwise register", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"REPLACED"}),
		)
		sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "REPLACED world" {
			t.Errorf("Line(0) = %q, want 'REPLACED world'", m.Line(0))
		}
	})

	t.Run("vp replaces single character", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "v", "p") // select "h", paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "Xello world" {
			t.Errorf("Line(0) = %q, want 'Xello world'", m.Line(0))
		}
	})

	t.Run("vp replaces word in middle of line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world goodbye"}),
			WithCursorPos(action.Position{Line: 0, Col: 6}),
			WithRegister('"', action.CharwiseRegister, []string{"EARTH"}),
		)
		sendKeys(tm, "v", "e", "p") // select "world", paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello EARTH goodbye" {
			t.Errorf("Line(0) = %q, want 'hello EARTH goodbye'", m.Line(0))
		}
	})

	t.Run("vp replaces to end of line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 6}),
			WithRegister('"', action.CharwiseRegister, []string{"universe"}),
		)
		sendKeys(tm, "v", "$", "p") // select "world", paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "hello universe" {
			t.Errorf("Line(0) = %q, want 'hello universe'", m.Line(0))
		}
	})

	t.Run("vp with empty register deletes selection", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{""}),
		)
		sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste empty

		m := getFinalModel(t, tm)
		if m.Line(0) != " world" {
			t.Errorf("Line(0) = %q, want ' world'", m.Line(0))
		}
	})

	// -------------------------------------------------------------------------
	// Visual Mode Paste Spanning Multiple Lines
	// -------------------------------------------------------------------------

	t.Run("vp replaces selection spanning multiple lines", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two", "line three"}),
			WithCursorPos(action.Position{Line: 0, Col: 5}),
			WithRegister('"', action.CharwiseRegister, []string{"REPLACED"}),
		)
		// v at (0,5), j goes to (1,5), h goes to (1,4) = space after "line"
		// Selection: "one\nline " (from 'o' to space)
		sendKeys(tm, "v", "j", "h", "p")

		m := getFinalModel(t, tm)
		// Should join: "line " + "REPLACED" + "two"
		if m.Line(0) != "line REPLACEDtwo" {
			t.Errorf("Line(0) = %q, want 'line REPLACEDtwo'", m.Line(0))
		}
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
	})

	// -------------------------------------------------------------------------
	// Cursor Position After Visual Paste
	// -------------------------------------------------------------------------

	t.Run("vp cursor at start of pasted content", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 6}),
			WithRegister('"', action.CharwiseRegister, []string{"EARTH"}),
		)
		sendKeys(tm, "v", "e", "p") // select "world", paste "EARTH"

		m := getFinalModel(t, tm)
		// Cursor should be at start of pasted content (or end, depending on impl)
		// In Vim, cursor goes to last char of pasted text
		if m.CursorX() != 10 {
			t.Errorf("CursorX() = %d, want 10", m.CursorX())
		}
	})

	t.Run("vp exits visual mode", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "v", "l", "p")

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

	// -------------------------------------------------------------------------
	// Visual Mode Paste with Linewise Register
	// -------------------------------------------------------------------------

	t.Run("vp with linewise register replaces selection", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"NEW LINE"}),
		)
		sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste linewise content

		m := getFinalModel(t, tm)
		// Linewise register content is pasted inline replacing the selection
		if m.Line(0) != "NEW LINE world" {
			t.Errorf("Line(0) = %q, want 'NEW LINE world'", m.Line(0))
		}
	})
}

func TestVisualLinePaste(t *testing.T) {
	// -------------------------------------------------------------------------
	// Visual Line Mode Paste
	// -------------------------------------------------------------------------

	t.Run("Vp replaces line with charwise register", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two", "line three"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"REPLACED"}),
		)
		sendKeys(tm, "V", "p") // select line two, paste

		m := getFinalModel(t, tm)
		if m.LineCount() != 3 {
			t.Errorf("LineCount() = %d, want 3", m.LineCount())
		}
		if m.Line(1) != "REPLACED" {
			t.Errorf("Line(1) = %q, want 'REPLACED'", m.Line(1))
		}
	})

	t.Run("Vp replaces line with linewise register", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two", "line three"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"NEW LINE"}),
		)
		sendKeys(tm, "V", "p") // select line two, paste

		m := getFinalModel(t, tm)
		if m.LineCount() != 3 {
			t.Errorf("LineCount() = %d, want 3", m.LineCount())
		}
		if m.Line(1) != "NEW LINE" {
			t.Errorf("Line(1) = %q, want 'NEW LINE'", m.Line(1))
		}
	})

	t.Run("Vp replaces multiple lines with linewise register", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two", "line three", "line four"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"REPLACED"}),
		)
		sendKeys(tm, "V", "j", "p") // select lines two and three, paste

		m := getFinalModel(t, tm)
		if m.LineCount() != 3 {
			t.Errorf("LineCount() = %d, want 3", m.LineCount())
		}
		if m.Line(0) != "line one" {
			t.Errorf("Line(0) = %q, want 'line one'", m.Line(0))
		}
		if m.Line(1) != "REPLACED" {
			t.Errorf("Line(1) = %q, want 'REPLACED'", m.Line(1))
		}
		if m.Line(2) != "line four" {
			t.Errorf("Line(2) = %q, want 'line four'", m.Line(2))
		}
	})

	t.Run("Vp replaces multiple lines with multiple linewise register lines", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two", "line three", "line four"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"NEW A", "NEW B"}),
		)
		sendKeys(tm, "V", "j", "p") // select lines two and three, paste two lines

		m := getFinalModel(t, tm)
		if m.LineCount() != 4 {
			t.Errorf("LineCount() = %d, want 4", m.LineCount())
		}
		if m.Line(0) != "line one" {
			t.Errorf("Line(0) = %q, want 'line one'", m.Line(0))
		}
		if m.Line(1) != "NEW A" {
			t.Errorf("Line(1) = %q, want 'NEW A'", m.Line(1))
		}
		if m.Line(2) != "NEW B" {
			t.Errorf("Line(2) = %q, want 'NEW B'", m.Line(2))
		}
		if m.Line(3) != "line four" {
			t.Errorf("Line(3) = %q, want 'line four'", m.Line(3))
		}
	})

	t.Run("Vp on first line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"FIRST"}),
		)
		sendKeys(tm, "V", "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
		if m.Line(0) != "FIRST" {
			t.Errorf("Line(0) = %q, want 'FIRST'", m.Line(0))
		}
		if m.Line(1) != "line two" {
			t.Errorf("Line(1) = %q, want 'line two'", m.Line(1))
		}
	})

	t.Run("Vp on last line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two"}),
			WithCursorPos(action.Position{Line: 1, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"LAST"}),
		)
		sendKeys(tm, "V", "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 2 {
			t.Errorf("LineCount() = %d, want 2", m.LineCount())
		}
		if m.Line(0) != "line one" {
			t.Errorf("Line(0) = %q, want 'line one'", m.Line(0))
		}
		if m.Line(1) != "LAST" {
			t.Errorf("Line(1) = %q, want 'LAST'", m.Line(1))
		}
	})

	t.Run("Vp exits visual line mode", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"REPLACED"}),
		)
		sendKeys(tm, "V", "p")

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

func TestVisualPasteRegisterBehavior(t *testing.T) {
	// -------------------------------------------------------------------------
	// Register Behavior - deleted text should go to register
	// -------------------------------------------------------------------------

	t.Run("vp puts deleted text into register", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"NEW"}),
		)
		sendKeys(tm, "v", "l", "l", "l", "l", "p") // select "hello", paste "NEW"

		m := getFinalModel(t, tm)
		// After visual paste, the deleted "hello" should be in the register
		reg, ok := m.GetRegister('"')
		if !ok {
			t.Fatal("unnamed register not found")
		}
		if len(reg.Content) != 1 || reg.Content[0] != "hello" {
			t.Errorf("register content = %q, want 'hello'", reg.Content)
		}
	})

	t.Run("Vp puts deleted line into register", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"line one", "line two"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"REPLACED"}),
		)
		sendKeys(tm, "V", "p")

		m := getFinalModel(t, tm)
		reg, ok := m.GetRegister('"')
		if !ok {
			t.Fatal("unnamed register not found")
		}
		if len(reg.Content) != 1 || reg.Content[0] != "line one" {
			t.Errorf("register content = %q, want 'line one'", reg.Content)
		}
		if reg.Type != action.LinewiseRegister {
			t.Errorf("register type = %v, want LinewiseRegister", reg.Type)
		}
	})
}

func TestVisualPasteEdgeCases(t *testing.T) {
	// -------------------------------------------------------------------------
	// Edge Cases
	// -------------------------------------------------------------------------

	t.Run("vp on empty line", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"", "line two"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"NEW"}),
		)
		sendKeys(tm, "v", "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "NEW" {
			t.Errorf("Line(0) = %q, want 'NEW'", m.Line(0))
		}
	})

	t.Run("vp selecting entire line content", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello", "world"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"REPLACED"}),
		)
		sendKeys(tm, "v", "$", "p") // select entire "hello"

		m := getFinalModel(t, tm)
		if m.Line(0) != "REPLACED" {
			t.Errorf("Line(0) = %q, want 'REPLACED'", m.Line(0))
		}
	})

	t.Run("vp with backwards selection", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"hello world"}),
			WithCursorPos(action.Position{Line: 0, Col: 5}),
			WithRegister('"', action.CharwiseRegister, []string{"X"}),
		)
		sendKeys(tm, "v", "h", "h", "h", "h", "h", "p") // select backwards "hello ", paste

		m := getFinalModel(t, tm)
		if m.Line(0) != "Xworld" {
			t.Errorf("Line(0) = %q, want 'Xworld'", m.Line(0))
		}
	})

	t.Run("vp in single character file", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"a"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.CharwiseRegister, []string{"XYZ"}),
		)
		sendKeys(tm, "v", "p")

		m := getFinalModel(t, tm)
		if m.Line(0) != "XYZ" {
			t.Errorf("Line(0) = %q, want 'XYZ'", m.Line(0))
		}
	})

	t.Run("Vp on single line file", func(t *testing.T) {
		tm := newTestModel(t,
			WithLines([]string{"only line"}),
			WithCursorPos(action.Position{Line: 0, Col: 0}),
			WithRegister('"', action.LinewiseRegister, []string{"REPLACED"}),
		)
		sendKeys(tm, "V", "p")

		m := getFinalModel(t, tm)
		if m.LineCount() != 1 {
			t.Errorf("LineCount() = %d, want 1", m.LineCount())
		}
		if m.Line(0) != "REPLACED" {
			t.Errorf("Line(0) = %q, want 'REPLACED'", m.Line(0))
		}
	})
}
