"""Tests for output filter system."""

import re

from terminaluse.lib.utils.output import (
    BlockRemoveFilter,
    FilterPipeline,
    LineFilter,
    LinePrefixStripFilter,
    RegexRemoveFilter,
)


class TestRegexRemoveFilter:
    """Tests for RegexRemoveFilter."""

    def test_removes_matching_content(self) -> None:
        """Removes content matching the pattern."""
        pattern = re.compile(r"<tag>.*?</tag>", re.DOTALL)
        f = RegexRemoveFilter(pattern)

        result = f.apply("before <tag>secret</tag> after")
        assert result == "before  after"

    def test_removes_multiline_content(self) -> None:
        """Removes content spanning multiple lines."""
        pattern = re.compile(r"<block>.*?</block>", re.DOTALL)
        f = RegexRemoveFilter(pattern)

        text = "before\n<block>\nline1\nline2\n</block>\nafter"
        result = f.apply(text)
        assert result == "before\n\nafter"

    def test_no_match_unchanged(self) -> None:
        """Text without matches is unchanged."""
        pattern = re.compile(r"<tag>.*?</tag>")
        f = RegexRemoveFilter(pattern)

        result = f.apply("no tags here")
        assert result == "no tags here"


class TestLineFilter:
    """Tests for LineFilter."""

    def test_keeps_matching_lines(self) -> None:
        """Keeps lines where predicate returns True."""
        f = LineFilter(lambda line: "keep" in line)

        text = "keep this\nremove this\nkeep this too"
        result = f.apply(text)
        assert result == "keep this\nkeep this too"

    def test_removes_metadata_lines(self) -> None:
        """Removes lines starting with metadata prefixes."""

        def not_metadata(line: str) -> bool:
            return not line.strip().startswith("agentId:")

        f = LineFilter(not_metadata)

        text = "Hello\nagentId: abc123\nWorld"
        result = f.apply(text)
        assert result == "Hello\nWorld"

    def test_empty_result_if_all_filtered(self) -> None:
        """Returns empty string if all lines filtered."""
        f = LineFilter(lambda _: False)

        result = f.apply("line1\nline2\nline3")
        assert result == ""


class TestLinePrefixStripFilter:
    """Tests for LinePrefixStripFilter."""

    def test_strips_line_number_prefix(self) -> None:
        """Strips line number prefixes from each line."""
        pattern = re.compile(r"^\s*\d+→")
        f = LinePrefixStripFilter(pattern)

        text = "  42→content here\n  43→more content"
        result = f.apply(text)
        assert result == "content here\nmore content"

    def test_preserves_lines_without_prefix(self) -> None:
        """Lines without the prefix are preserved."""
        pattern = re.compile(r"^\s*\d+→")
        f = LinePrefixStripFilter(pattern)

        text = "no prefix here\n  42→has prefix"
        result = f.apply(text)
        assert result == "no prefix here\nhas prefix"


class TestBlockRemoveFilter:
    """Tests for BlockRemoveFilter."""

    def test_removes_simple_block(self) -> None:
        """Removes content between markers."""
        f = BlockRemoveFilter("<secret>", "</secret>")

        text = "before <secret>hidden</secret> after"
        result = f.apply(text)
        assert "hidden" not in result
        assert "before" in result
        assert "after" in result

    def test_removes_multiline_block(self) -> None:
        """Removes blocks spanning multiple lines."""
        f = BlockRemoveFilter("<remove>", "</remove>")

        text = "keep this\n<remove>\nhidden1\nhidden2\n</remove>\nkeep this too"
        result = f.apply(text)
        assert "hidden" not in result
        assert "keep this" in result
        assert "keep this too" in result

    def test_handles_same_line_markers(self) -> None:
        """Handles both markers on the same line."""
        f = BlockRemoveFilter("<x>", "</x>")

        text = "before <x>middle</x> after"
        result = f.apply(text)
        assert result == "before  after"

    def test_no_markers_unchanged(self) -> None:
        """Text without markers is unchanged."""
        f = BlockRemoveFilter("<x>", "</x>")

        text = "no markers here"
        result = f.apply(text)
        assert result == "no markers here"


class TestFilterPipeline:
    """Tests for FilterPipeline."""

    def test_applies_filters_in_order(self) -> None:
        """Filters are applied in sequence."""
        pipeline = FilterPipeline(
            [
                RegexRemoveFilter(re.compile(r"<a>.*?</a>")),
                RegexRemoveFilter(re.compile(r"<b>.*?</b>")),
            ]
        )

        text = "keep <a>remove</a> also <b>remove</b> keep"
        result = pipeline.apply(text)
        assert result == "keep  also  keep"

    def test_empty_pipeline(self) -> None:
        """Empty pipeline returns input unchanged."""
        pipeline = FilterPipeline()

        result = pipeline.apply("unchanged")
        assert result == "unchanged"

    def test_with_filter_creates_new_pipeline(self) -> None:
        """with_filter returns new pipeline, original unchanged."""
        original = FilterPipeline([])
        extended = original.with_filter(RegexRemoveFilter(re.compile(r"x")))

        assert len(original) == 0
        assert len(extended) == 1

    def test_with_filters_appends_multiple(self) -> None:
        """with_filters appends multiple filters."""
        pipeline = FilterPipeline([])
        extended = pipeline.with_filters(
            [
                RegexRemoveFilter(re.compile(r"a")),
                RegexRemoveFilter(re.compile(r"b")),
            ]
        )

        assert len(extended) == 2

    def test_complex_pipeline(self) -> None:
        """Complex pipeline with multiple filter types."""
        pipeline = FilterPipeline(
            [
                RegexRemoveFilter(re.compile(r"<system>.*?</system>", re.DOTALL)),
                LineFilter(lambda line: "metadata:" not in line),
                LinePrefixStripFilter(re.compile(r"^\d+\|")),
            ]
        )

        text = "1|content\n<system>hidden</system>\nmetadata: value\n2|more"
        result = pipeline.apply(text)
        assert result == "content\n\nmore"
