"""Tests for sort command functionality."""

import pytest

from e3po.editor import Editor, MarkMode, Mark


class MockScreen:
    """Mock curses screen for testing."""
    def __init__(self):
        self.height = 24
        self.width = 80

    def getmaxyx(self):
        return (self.height, self.width)

    def clear(self):
        pass

    def refresh(self):
        pass

    def addstr(self, *args):
        pass

    def move(self, row, col):
        pass

    def getch(self):
        return -1


def make_editor(lines=None):
    """Create an editor with mock screen for testing."""
    editor = Editor()
    editor.screen = MockScreen()
    editor.height = 24
    editor.width = 80
    editor.edit_height = 20
    editor.top_row = 0
    editor.left_col = 0
    if lines:
        editor.buffer.lines = lines.copy()
    return editor


class TestSortNoMark:
    """Test sorting without any mark (entire file)."""

    def test_sort_entire_file(self):
        editor = make_editor(["cherry", "apple", "banana"])
        editor.do_sort_command("sort")
        assert editor.buffer.lines == ["apple", "banana", "cherry"]

    def test_sort_reverse(self):
        editor = make_editor(["apple", "banana", "cherry"])
        editor.do_sort_command("sort -r")
        assert editor.buffer.lines == ["cherry", "banana", "apple"]

    def test_sort_numeric(self):
        editor = make_editor(["10", "2", "1", "20"])
        editor.do_sort_command("sort -n")
        assert editor.buffer.lines == ["1", "2", "10", "20"]

    def test_sort_case_insensitive(self):
        editor = make_editor(["Banana", "apple", "Cherry"])
        editor.do_sort_command("sort -f")
        # Case insensitive sort
        assert editor.buffer.lines[0].lower() == "apple"


class TestSortLineMark:
    """Test sorting with LINE mark."""

    def test_sort_marked_lines_only(self):
        editor = make_editor(["header", "cherry", "apple", "banana", "footer"])
        # Mark lines 1-3 (cherry, apple, banana)
        editor.mark = Mark(
            mode=MarkMode.LINE,
            start_row=1,
            start_col=0,
            end_row=3,
            end_col=0,
            pending=False
        )
        editor.do_sort_command("sort")
        assert editor.buffer.lines == ["header", "apple", "banana", "cherry", "footer"]

    def test_sort_marked_lines_reverse(self):
        editor = make_editor(["header", "apple", "banana", "cherry", "footer"])
        # Mark lines 1-3
        editor.mark = Mark(
            mode=MarkMode.LINE,
            start_row=1,
            start_col=0,
            end_row=3,
            end_col=0,
            pending=False
        )
        editor.do_sort_command("sort -r")
        assert editor.buffer.lines == ["header", "cherry", "banana", "apple", "footer"]

    def test_sort_clears_mark(self):
        editor = make_editor(["cherry", "apple", "banana"])
        editor.mark = Mark(
            mode=MarkMode.LINE,
            start_row=0,
            start_col=0,
            end_row=2,
            end_col=0,
            pending=False
        )
        editor.do_sort_command("sort")
        assert editor.mark is None


class TestSortBlockMark:
    """Test sorting with BLOCK mark (sort by block columns)."""

    def test_sort_by_block_column(self):
        # Lines with format: "name  number"
        # Sort by the number column (cols 6-7)
        editor = make_editor([
            "alice 30",
            "bob   10",
            "carol 20"
        ])
        # Block mark on columns 6-7 (the numbers)
        editor.mark = Mark(
            mode=MarkMode.BLOCK,
            start_row=0,
            start_col=6,
            end_row=2,
            end_col=7,
            pending=False
        )
        editor.do_sort_command("sort")
        # Should sort by the number column: 10, 20, 30
        assert editor.buffer.lines[0] == "bob   10"
        assert editor.buffer.lines[1] == "carol 20"
        assert editor.buffer.lines[2] == "alice 30"

    def test_sort_by_block_column_numeric(self):
        editor = make_editor([
            "item  100",
            "item  20",
            "item  3"
        ])
        # Block mark on number column
        editor.mark = Mark(
            mode=MarkMode.BLOCK,
            start_row=0,
            start_col=6,
            end_row=2,
            end_col=8,
            pending=False
        )
        editor.do_sort_command("sort -n")
        # Numeric sort: 3, 20, 100
        assert "3" in editor.buffer.lines[0]
        assert "20" in editor.buffer.lines[1]
        assert "100" in editor.buffer.lines[2]


class TestSortByField:
    """Test sorting by field (-k option)."""

    def test_sort_by_field_k2(self):
        editor = make_editor(["zebra 1", "apple 3", "mango 2"])
        editor.do_sort_command("sort -k2")
        # Sort by second field: 1, 2, 3
        assert editor.buffer.lines == ["zebra 1", "mango 2", "apple 3"]

    def test_sort_by_field_k2_numeric(self):
        editor = make_editor(["a 10", "b 2", "c 1"])
        editor.do_sort_command("sort -k2 -n")
        # Numeric sort by second field
        assert editor.buffer.lines == ["c 1", "b 2", "a 10"]

    def test_sort_by_field_k1(self):
        editor = make_editor(["zebra 1", "apple 3", "mango 2"])
        editor.do_sort_command("sort -k1")
        # Sort by first field (alphabetically)
        assert editor.buffer.lines == ["apple 3", "mango 2", "zebra 1"]

    def test_sort_by_multiple_fields(self):
        editor = make_editor([
            "bob 2 x",
            "alice 1 y",
            "bob 1 z",
            "alice 2 w"
        ])
        editor.do_sort_command("sort -k1 -k2")
        # Sort by field 1, then field 2
        assert editor.buffer.lines == [
            "alice 1 y",
            "alice 2 w",
            "bob 1 z",
            "bob 2 x"
        ]

    def test_sort_by_multiple_fields_numeric(self):
        # When -n is used, it applies to all keys
        editor = make_editor([
            "2 10",
            "1 20",
            "2 5"
        ])
        editor.do_sort_command("sort -k1 -k2 -n")
        # Sort by field 1 (numeric), then field 2 (numeric)
        assert editor.buffer.lines == [
            "1 20",
            "2 5",
            "2 10"
        ]


class TestSortUndo:
    """Test that sort can be undone."""

    def test_sort_creates_undo(self):
        editor = make_editor(["cherry", "apple", "banana"])
        original = editor.buffer.lines.copy()
        editor.do_sort_command("sort")
        assert editor.buffer.lines == ["apple", "banana", "cherry"]
        # Undo should restore original order
        editor.action_undo()
        assert editor.buffer.lines == original

    def test_sort_marks_modified(self):
        editor = make_editor(["cherry", "apple", "banana"])
        editor.buffer.modified = False
        editor.do_sort_command("sort")
        assert editor.buffer.modified == True


class TestSortEdgeCases:
    """Test edge cases and combined options."""

    def test_sort_single_line(self):
        """Sorting a single line should not crash."""
        editor = make_editor(["only one line"])
        editor.do_sort_command("sort")
        assert editor.buffer.lines == ["only one line"]

    def test_sort_empty_file(self):
        """Sorting an empty file should not crash."""
        editor = make_editor([""])
        editor.do_sort_command("sort")
        assert editor.buffer.lines == [""]

    def test_sort_already_sorted(self):
        """Sorting already sorted lines should work."""
        editor = make_editor(["apple", "banana", "cherry"])
        editor.do_sort_command("sort")
        assert editor.buffer.lines == ["apple", "banana", "cherry"]

    def test_sort_reverse_case_insensitive(self):
        """Test -r and -f combined."""
        editor = make_editor(["Apple", "banana", "Cherry"])
        editor.do_sort_command("sort -r -f")
        # Reverse case-insensitive: cherry, banana, apple
        assert editor.buffer.lines[0].lower() == "cherry"
        assert editor.buffer.lines[2].lower() == "apple"

    def test_sort_numeric_reverse(self):
        """Test -n and -r combined."""
        editor = make_editor(["1", "10", "2", "20"])
        editor.do_sort_command("sort -n -r")
        assert editor.buffer.lines == ["20", "10", "2", "1"]

    def test_sort_with_empty_lines(self):
        """Empty lines should sort to the beginning."""
        editor = make_editor(["cherry", "", "apple", "", "banana"])
        editor.do_sort_command("sort")
        # Empty strings sort before non-empty
        assert editor.buffer.lines[0] == ""
        assert editor.buffer.lines[1] == ""
        assert "apple" in editor.buffer.lines

    def test_sort_preserves_lines_outside_mark(self):
        """Lines outside LINE mark should not be affected."""
        editor = make_editor(["first", "cherry", "apple", "banana", "last"])
        editor.mark = Mark(
            mode=MarkMode.LINE,
            start_row=1,
            start_col=0,
            end_row=3,
            end_col=0,
            pending=False
        )
        editor.do_sort_command("sort")
        assert editor.buffer.lines[0] == "first"
        assert editor.buffer.lines[4] == "last"
        # Middle should be sorted
        assert editor.buffer.lines[1:4] == ["apple", "banana", "cherry"]

    def test_sort_by_field_with_irregular_spacing(self):
        """Test sorting by field with varying whitespace."""
        editor = make_editor([
            "name   30",
            "other 10",
            "test    20"
        ])
        editor.do_sort_command("sort -k2 -n")
        # Should sort by second field (numbers)
        assert "10" in editor.buffer.lines[0]
        assert "20" in editor.buffer.lines[1]
        assert "30" in editor.buffer.lines[2]

    def test_sort_duplicates(self):
        """Duplicate lines should be preserved."""
        editor = make_editor(["apple", "banana", "apple", "cherry", "banana"])
        editor.do_sort_command("sort")
        assert editor.buffer.lines == ["apple", "apple", "banana", "banana", "cherry"]
