"""Tests for grep 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 TestGrepNoMark:
    """Test grep without any mark (entire file)."""

    def test_grep_keeps_matching_lines(self):
        editor = make_editor(["apple pie", "banana bread", "apple tart", "cherry"])
        editor.do_grep_command("grep apple")
        assert editor.buffer.lines == ["apple pie", "apple tart"]

    def test_grep_no_matches(self):
        editor = make_editor(["apple", "banana", "cherry"])
        editor.do_grep_command("grep xyz")
        # No changes when no matches
        assert editor.buffer.lines == ["apple", "banana", "cherry"]
        assert "No matches" in editor.message

    def test_grep_all_match(self):
        editor = make_editor(["apple", "pineapple", "apple pie"])
        editor.do_grep_command("grep apple")
        assert editor.buffer.lines == ["apple", "pineapple", "apple pie"]

    def test_grep_inverse(self):
        editor = make_editor(["apple", "banana", "cherry", "apricot"])
        editor.do_grep_command("grep -v a")
        # Keep lines without 'a'
        assert editor.buffer.lines == ["cherry"]

    def test_grep_case_sensitive(self):
        editor = make_editor(["Apple", "apple", "APPLE"])
        editor.do_grep_command("grep apple")
        assert editor.buffer.lines == ["apple"]

    def test_grep_case_insensitive(self):
        editor = make_editor(["Apple", "apple", "APPLE", "banana"])
        editor.do_grep_command("grep -i apple")
        assert editor.buffer.lines == ["Apple", "apple", "APPLE"]

    def test_grep_inverse_case_insensitive(self):
        editor = make_editor(["Apple", "banana", "APPLE"])
        editor.do_grep_command("grep -vi apple")
        assert editor.buffer.lines == ["banana"]

    def test_grep_regex(self):
        editor = make_editor(["test123", "test", "123test", "hello"])
        editor.do_grep_command("grep test[0-9]")
        assert editor.buffer.lines == ["test123"]


class TestGrepWithLineMark:
    """Test grep with LINE mark."""

    def test_grep_marked_lines_only(self):
        editor = make_editor(["header", "apple", "banana", "apple", "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_grep_command("grep apple")
        # Should only grep within mark, header and footer unchanged
        assert editor.buffer.lines == ["header", "apple", "apple", "footer"]

    def test_grep_preserves_lines_outside_mark(self):
        editor = make_editor(["keep", "apple", "banana", "cherry", "keep"])
        editor.mark = Mark(
            mode=MarkMode.LINE,
            start_row=1,
            start_col=0,
            end_row=3,
            end_col=0,
            pending=False
        )
        editor.do_grep_command("grep -v a")  # Remove lines with 'a'
        # First and last 'keep' lines should remain
        assert editor.buffer.lines[0] == "keep"
        assert editor.buffer.lines[-1] == "keep"
        assert "cherry" in editor.buffer.lines

    def test_grep_clears_mark(self):
        editor = make_editor(["apple", "banana"])
        editor.mark = Mark(
            mode=MarkMode.LINE,
            start_row=0,
            start_col=0,
            end_row=1,
            end_col=0,
            pending=False
        )
        editor.do_grep_command("grep apple")
        assert editor.mark is None


class TestGrepWithBlockMark:
    """Test grep with BLOCK mark (search within columns)."""

    def test_grep_block_searches_columns_only(self):
        # Lines with format: "prefix:data"
        # Block mark on the data portion
        editor = make_editor([
            "AAA:apple",
            "BBB:banana",
            "CCC:apple",
            "DDD:cherry"
        ])
        # Block mark on columns 4-8 (the fruit part after colon)
        editor.mark = Mark(
            mode=MarkMode.BLOCK,
            start_row=0,
            start_col=4,
            end_row=3,
            end_col=8,
            pending=False
        )
        editor.do_grep_command("grep apple")
        # Should match based on block content, but keep full lines
        assert len(editor.buffer.lines) == 2
        assert "AAA:apple" in editor.buffer.lines
        assert "CCC:apple" in editor.buffer.lines

    def test_grep_block_keeps_full_lines(self):
        editor = make_editor([
            "col1 match col3",
            "col1 xxxxx col3",
            "col1 match col3"
        ])
        # Block mark on columns 5-9 (the middle word)
        editor.mark = Mark(
            mode=MarkMode.BLOCK,
            start_row=0,
            start_col=5,
            end_row=2,
            end_col=9,
            pending=False
        )
        editor.do_grep_command("grep match")
        # Full lines are kept
        assert editor.buffer.lines == ["col1 match col3", "col1 match col3"]


class TestGrepUndo:
    """Test that grep can be undone."""

    def test_grep_creates_undo(self):
        editor = make_editor(["apple", "banana", "cherry"])
        original = editor.buffer.lines.copy()
        editor.do_grep_command("grep apple")
        assert editor.buffer.lines == ["apple"]
        # Undo should restore original
        editor.action_undo()
        assert editor.buffer.lines == original

    def test_grep_marks_modified(self):
        editor = make_editor(["apple", "banana"])
        editor.buffer.modified = False
        editor.do_grep_command("grep apple")
        assert editor.buffer.modified == True


class TestGrepEdgeCases:
    """Test edge cases."""

    def test_grep_single_line_match(self):
        editor = make_editor(["only line"])
        editor.do_grep_command("grep only")
        assert editor.buffer.lines == ["only line"]

    def test_grep_single_line_no_match(self):
        editor = make_editor(["only line"])
        editor.do_grep_command("grep xyz")
        assert editor.buffer.lines == ["only line"]
        assert "No matches" in editor.message

    def test_grep_empty_pattern_error(self):
        editor = make_editor(["test"])
        editor.do_grep_command("grep")
        assert "Usage" in editor.message

    def test_grep_special_chars(self):
        """Test grep with special regex characters."""
        editor = make_editor(["test.txt", "test-txt", "testtxt"])
        editor.do_grep_command("grep test\\.txt")
        assert editor.buffer.lines == ["test.txt"]
