"""Tests for the content scrubber analyzer."""

import pytest

from turboseo.analyzers.content_scrubber import (
    EmDashReplacement,
    ScrubResult,
    scrub_content,
)


class TestWatermarkRemoval:
    """Tests for watermark character removal."""

    def test_removes_zero_width_space(self):
        """Test removal of zero-width space (U+200B)."""
        content = "Hello\u200Bworld"
        result = scrub_content(content)
        assert "\u200B" not in result.scrubbed_content
        assert result.watermarks_removed >= 1

    def test_removes_byte_order_mark(self):
        """Test removal of BOM (U+FEFF)."""
        content = "\uFEFFHello world"
        result = scrub_content(content)
        assert "\uFEFF" not in result.scrubbed_content
        assert result.watermarks_removed >= 1

    def test_removes_zero_width_non_joiner(self):
        """Test removal of zero-width non-joiner (U+200C)."""
        content = "Hello\u200Cworld"
        result = scrub_content(content)
        assert "\u200C" not in result.scrubbed_content

    def test_removes_word_joiner(self):
        """Test removal of word joiner (U+2060)."""
        content = "Hello\u2060world"
        result = scrub_content(content)
        assert "\u2060" not in result.scrubbed_content

    def test_removes_soft_hyphen(self):
        """Test removal of soft hyphen (U+00AD)."""
        content = "Hello\u00ADworld"
        result = scrub_content(content)
        assert "\u00AD" not in result.scrubbed_content

    def test_removes_narrow_no_break_space(self):
        """Test removal of narrow no-break space (U+202F)."""
        content = "Hello\u202Fworld"
        result = scrub_content(content)
        assert "\u202F" not in result.scrubbed_content

    def test_preserves_word_separation(self):
        """Test that zero-width space between words becomes regular space."""
        content = "Hello\u200Bworld"
        result = scrub_content(content)
        assert result.scrubbed_content == "Hello world"

    def test_counts_watermark_types(self):
        """Test that watermark types are counted correctly."""
        content = "A\u200BB\u200BC\uFEFFD"
        result = scrub_content(content)
        assert "Zero-width space" in result.watermark_types
        assert result.watermark_types["Zero-width space"] == 2
        assert "Byte Order Mark" in result.watermark_types

    def test_multiple_watermark_types(self):
        """Test removal of multiple watermark types."""
        content = "\uFEFFHello\u200Bworld\u200C!"
        result = scrub_content(content)
        assert result.watermarks_removed == 3
        assert len(result.watermark_types) >= 2

    def test_no_watermarks_clean_content(self):
        """Test clean content has no watermarks removed."""
        content = "Hello world, this is clean content."
        result = scrub_content(content)
        assert result.watermarks_removed == 0
        assert result.watermark_types == {}


class TestEmDashReplacement:
    """Tests for em-dash replacement."""

    def test_replaces_em_dash(self):
        """Test basic em-dash replacement."""
        content = "The fox\u2014quick and clever\u2014ran fast."
        result = scrub_content(content)
        assert "\u2014" not in result.scrubbed_content
        assert result.em_dashes_replaced == 2

    def test_replaces_double_hyphen(self):
        """Test replacement of double hyphen as em-dash."""
        content = "The fox--quick and clever--ran fast."
        result = scrub_content(content)
        assert "--" not in result.scrubbed_content
        assert result.em_dashes_replaced == 2

    def test_attribution_context_comma(self):
        """Test em-dash in attribution context becomes comma."""
        content = '"This is important"\u2014said the expert.'
        result = scrub_content(content)
        assert ", " in result.scrubbed_content or "," in result.scrubbed_content

    def test_conjunctive_adverb_semicolon(self):
        """Test em-dash before conjunctive adverb becomes semicolon."""
        content = "The data supports this\u2014however, we need more research."
        result = scrub_content(content)
        assert "; however" in result.scrubbed_content

    def test_independent_clause_period(self):
        """Test em-dash before independent clause becomes period."""
        content = "The first point is clear\u2014The second follows naturally."
        result = scrub_content(content)
        assert ". The" in result.scrubbed_content

    def test_short_aside_comma(self):
        """Test short parenthetical aside gets comma."""
        content = "The quick fox\u2014agile\u2014ran."
        result = scrub_content(content)
        # Short asides should become commas
        assert "," in result.scrubbed_content

    def test_replacement_details_tracked(self):
        """Test that replacement details are tracked."""
        content = "Hello\u2014world"
        result = scrub_content(content)
        assert len(result.replacements) == 1
        assert isinstance(result.replacements[0], EmDashReplacement)
        assert result.replacements[0].line >= 1
        assert result.replacements[0].reason in [
            "attribution",
            "conjunctive_adverb",
            "independent_clause",
            "list_context",
            "short_aside",
            "default",
        ]

    def test_however_semicolon(self):
        """Test 'however' gets semicolon."""
        content = "This is true\u2014however, more data needed."
        result = scrub_content(content)
        assert "; however" in result.scrubbed_content

    def test_therefore_semicolon(self):
        """Test 'therefore' gets semicolon."""
        content = "The data shows\u2014therefore, we conclude."
        result = scrub_content(content)
        assert "; therefore" in result.scrubbed_content

    def test_moreover_semicolon(self):
        """Test 'moreover' gets semicolon."""
        content = "This works well\u2014moreover, it scales."
        result = scrub_content(content)
        assert "; moreover" in result.scrubbed_content


class TestScrubOptions:
    """Tests for scrub function options."""

    def test_disable_watermark_removal(self):
        """Test disabling watermark removal."""
        content = "Hello\u200Bworld"
        result = scrub_content(content, remove_watermarks=False)
        assert "\u200B" in result.scrubbed_content
        assert result.watermarks_removed == 0

    def test_disable_em_dash_replacement(self):
        """Test disabling em-dash replacement."""
        content = "Hello\u2014world"
        result = scrub_content(content, replace_em_dashes=False)
        assert "\u2014" in result.scrubbed_content
        assert result.em_dashes_replaced == 0

    def test_both_disabled(self):
        """Test both options disabled returns unchanged content."""
        content = "Hello\u200B\u2014world"
        result = scrub_content(content, remove_watermarks=False, replace_em_dashes=False)
        assert result.scrubbed_content == content

    def test_only_watermarks(self):
        """Test only watermark removal."""
        content = "Hello\u200B\u2014world"
        result = scrub_content(content, replace_em_dashes=False)
        assert "\u200B" not in result.scrubbed_content
        assert "\u2014" in result.scrubbed_content


class TestScrubResult:
    """Tests for the ScrubResult model."""

    def test_result_is_pydantic_model(self):
        """Test result is a proper Pydantic model."""
        result = scrub_content("Test content")
        assert isinstance(result, ScrubResult)

    def test_result_has_all_fields(self):
        """Test result has all required fields."""
        result = scrub_content("Test content")
        assert hasattr(result, "original_length")
        assert hasattr(result, "scrubbed_length")
        assert hasattr(result, "watermarks_removed")
        assert hasattr(result, "watermark_types")
        assert hasattr(result, "em_dashes_replaced")
        assert hasattr(result, "replacements")
        assert hasattr(result, "scrubbed_content")

    def test_result_serializable(self):
        """Test result can be serialized to dict."""
        result = scrub_content("Hello\u200Bworld\u2014test")
        result_dict = result.model_dump()
        assert isinstance(result_dict, dict)
        assert "scrubbed_content" in result_dict

    def test_length_tracking(self):
        """Test original and scrubbed length are tracked."""
        content = "Hello\u200Bworld"
        result = scrub_content(content)
        assert result.original_length == len(content)
        assert result.scrubbed_length <= result.original_length


class TestEdgeCases:
    """Tests for edge cases."""

    def test_empty_content(self):
        """Test handling of empty content."""
        result = scrub_content("")
        assert result.scrubbed_content == ""
        assert result.watermarks_removed == 0
        assert result.em_dashes_replaced == 0

    def test_whitespace_only(self):
        """Test handling of whitespace-only content."""
        result = scrub_content("   \n\n   ")
        assert result.scrubbed_content == "   \n\n   "

    def test_no_changes_needed(self):
        """Test content that needs no changes."""
        content = "This is perfectly clean content with no special characters."
        result = scrub_content(content)
        assert result.scrubbed_content == content
        assert result.watermarks_removed == 0
        assert result.em_dashes_replaced == 0

    def test_multiple_em_dashes_same_line(self):
        """Test multiple em-dashes on the same line."""
        content = "A\u2014B\u2014C\u2014D"
        result = scrub_content(content)
        assert result.em_dashes_replaced >= 3

    def test_em_dash_at_start(self):
        """Test em-dash at the start of content."""
        content = "\u2014Starting with a dash"
        result = scrub_content(content)
        assert "\u2014" not in result.scrubbed_content

    def test_em_dash_at_end(self):
        """Test em-dash at the end of content."""
        content = "Ending with a dash\u2014"
        result = scrub_content(content)
        assert "\u2014" not in result.scrubbed_content

    def test_multiline_content(self):
        """Test handling of multiline content."""
        content = "Line one\u2014with dash.\nLine two\u200Bwith watermark."
        result = scrub_content(content)
        assert "\u2014" not in result.scrubbed_content
        assert "\u200B" not in result.scrubbed_content
        assert "\n" in result.scrubbed_content

    def test_unicode_preserved(self):
        """Test that legitimate Unicode is preserved."""
        content = "Hello \u00e9\u00e8\u00ea world"  # accented e's
        result = scrub_content(content)
        assert "\u00e9" in result.scrubbed_content
        assert "\u00e8" in result.scrubbed_content

    def test_regular_hyphens_preserved(self):
        """Test that single hyphens are preserved."""
        content = "This is a well-known fact."
        result = scrub_content(content)
        assert result.scrubbed_content == content
        assert result.em_dashes_replaced == 0


class TestLineNumbers:
    """Tests for line number tracking in replacements."""

    def test_line_numbers_accurate(self):
        """Test that line numbers are accurate for replacements."""
        content = "Line one.\nLine two\u2014with dash.\nLine three."
        result = scrub_content(content)
        if result.replacements:
            # The em-dash is on line 2
            assert result.replacements[0].line == 2

    def test_multiple_lines_tracked(self):
        """Test replacements on multiple lines are tracked correctly."""
        content = "Line one\u2014dash.\nLine two\u2014another.\nLine three\u2014more."
        result = scrub_content(content)
        assert len(result.replacements) == 3
        lines = [r.line for r in result.replacements]
        assert lines == [1, 2, 3]
