package editor

import (
	"testing"

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

func TestMoveDown(t *testing.T) {
	t.Run("test 'j'", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "j")

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

	t.Run("test 'jjjj'", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "j", "j", "j", "j")

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

	t.Run("test 'jjjjjjjjj's with overflow", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "j", "j", "j", "j", "j", "j", "j", "j", "j")

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

func TestMoveDownWithCount(t *testing.T) {
	t.Run("test '3j'", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "3", "j")

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

	t.Run("test '10j' with overflow", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "1", "0", "j")

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

func TestMoveDownWithOverflow(t *testing.T) {
	lines := []string{"long line", "small"}

	t.Run("test 'j' with overflow", func(t *testing.T) {
		tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 8, Line: 0})
		sendKeys(tm, "j")

		m := getFinalModel(t, tm)
		want := len(lines[1])
		if m.CursorX() != want {
			t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
		}
	})

	t.Run("test 'j' without overflow", func(t *testing.T) {
		tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 3, Line: 0})
		sendKeys(tm, "j")

		m := getFinalModel(t, tm)
		if m.CursorX() != 3 {
			t.Errorf("cursor.x = %d, want 3", m.CursorX())
		}
	})
}

func TestMoveUp(t *testing.T) {
	t.Run("test 'k'", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 0, Line: 2})
		sendKeys(tm, "k")

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

	t.Run("test 'kkkk'", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 0, Line: 4})
		sendKeys(tm, "k", "k", "k", "k")

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

	t.Run("test 'k' at top (no movement)", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "k", "k", "k")

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

func TestMoveUpWithCount(t *testing.T) {
	t.Run("test '3k'", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 0, Line: 5})
		sendKeys(tm, "3", "k")

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

	t.Run("test '10k' with overflow", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 0, Line: 3})
		sendKeys(tm, "1", "0", "k")

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

func TestMoveUpWithOverflow(t *testing.T) {
	lines := []string{"small", "long line"}

	t.Run("test 'k' with overflow", func(t *testing.T) {
		tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 10, Line: 1})
		sendKeys(tm, "k")

		m := getFinalModel(t, tm)
		want := len(lines[0])
		if m.CursorX() != want {
			t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
		}
	})

	t.Run("test 'k' without overflow", func(t *testing.T) {
		tm := newTestModelWithLinesAndCursorPos(t, lines, action.Position{Col: 3, Line: 1})
		sendKeys(tm, "k")

		m := getFinalModel(t, tm)
		if m.CursorX() != 3 {
			t.Errorf("cursor.x = %d, want 3", m.CursorX())
		}
	})
}

func TestMoveRight(t *testing.T) {
	t.Run("test 'l'", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "l")

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

	t.Run("test 'llll'", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "l", "l", "l", "l")

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

	t.Run("test 'l' at end of line (no movement past end)", func(t *testing.T) {
		lines := []string{"abc"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, "l", "l", "l", "l", "l", "l")

		m := getFinalModel(t, tm)
		want := len(lines[0])
		if m.CursorX() != want {
			t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
		}
	})
}

func TestMoveRightWithCount(t *testing.T) {
	t.Run("test '3l'", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "3", "l")

		m := getFinalModel(t, tm)
		if m.CursorX() != 3 {
			t.Errorf("cursor.x = %d, want 3", m.CursorX())
		}
	})

	t.Run("test '10l' with overflow", func(t *testing.T) {
		lines := []string{"short"}
		tm := newTestModelWithLines(t, lines)
		sendKeys(tm, "1", "0", "l")

		m := getFinalModel(t, tm)
		want := len(lines[0])
		if m.CursorX() != want {
			t.Errorf("cursor.x = %d, want %d", m.CursorX(), want)
		}
	})
}

func TestMoveLeft(t *testing.T) {
	t.Run("test 'h'", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 3, Line: 0})
		sendKeys(tm, "h")

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

	t.Run("test 'hhhh'", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 4, Line: 0})
		sendKeys(tm, "h", "h", "h", "h")

		m := getFinalModel(t, tm)
		if m.CursorX() != 0 {
			t.Errorf("cursor.x = %d, want 0", m.CursorX())
		}
	})

	t.Run("test 'h' at start (no movement)", func(t *testing.T) {
		tm := newTestModel(t)
		sendKeys(tm, "h", "h", "h")

		m := getFinalModel(t, tm)
		if m.CursorX() != 0 {
			t.Errorf("cursor.x = %d, want 0", m.CursorX())
		}
	})
}

func TestMoveLeftWithCount(t *testing.T) {
	t.Run("test '3h'", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 5, Line: 0})
		sendKeys(tm, "3", "h")

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

	t.Run("test '10h' with overflow", func(t *testing.T) {
		tm := newTestModelWithCursorPos(t, action.Position{Col: 3, Line: 0})
		sendKeys(tm, "1", "0", "h")

		m := getFinalModel(t, tm)
		if m.CursorX() != 0 {
			t.Errorf("cursor.x = %d, want 0", m.CursorX())
		}
	})
}
