"""
Behavioral tests for citation_formatter module.

Tests CitationFormatter, QuartoExporter, RISExporter, and LaTeXExporter classes.
"""


class TestCitationModeEnum:
    """Tests for CitationMode enum."""

    def test_number_hyperlinks_mode_exists(self):
        """NUMBER_HYPERLINKS mode exists."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationMode,
        )

        assert hasattr(CitationMode, "NUMBER_HYPERLINKS")

    def test_domain_hyperlinks_mode_exists(self):
        """DOMAIN_HYPERLINKS mode exists."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationMode,
        )

        assert hasattr(CitationMode, "DOMAIN_HYPERLINKS")

    def test_domain_id_hyperlinks_mode_exists(self):
        """DOMAIN_ID_HYPERLINKS mode exists."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationMode,
        )

        assert hasattr(CitationMode, "DOMAIN_ID_HYPERLINKS")

    def test_domain_id_always_hyperlinks_mode_exists(self):
        """DOMAIN_ID_ALWAYS_HYPERLINKS mode exists."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationMode,
        )

        assert hasattr(CitationMode, "DOMAIN_ID_ALWAYS_HYPERLINKS")

    def test_no_hyperlinks_mode_exists(self):
        """NO_HYPERLINKS mode exists."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationMode,
        )

        assert hasattr(CitationMode, "NO_HYPERLINKS")

    def test_mode_values_are_strings(self):
        """All mode values are strings."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationMode,
        )

        for mode in CitationMode:
            assert isinstance(mode.value, str)


class TestCitationFormatterInit:
    """Tests for CitationFormatter initialization."""

    def test_default_mode_is_number_hyperlinks(self):
        """Default mode is NUMBER_HYPERLINKS."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter()
        assert formatter.mode == CitationMode.NUMBER_HYPERLINKS

    def test_accepts_custom_mode(self):
        """Accepts custom citation mode."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS)
        assert formatter.mode == CitationMode.DOMAIN_HYPERLINKS

    def test_has_citation_pattern(self):
        """Has compiled citation pattern regex."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        assert formatter.citation_pattern is not None

    def test_has_comma_citation_pattern(self):
        """Has compiled comma citation pattern regex."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        assert formatter.comma_citation_pattern is not None

    def test_has_source_word_pattern(self):
        """Has compiled source word pattern regex."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        assert formatter.source_word_pattern is not None


class TestCitationFormatterExtractDomain:
    """Tests for _extract_domain method."""

    def test_extracts_simple_domain(self):
        """Extracts simple domain from URL."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        result = formatter._extract_domain("https://example.com/page")
        assert result == "example.com"

    def test_removes_www_prefix(self):
        """Removes www. prefix from domain."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        result = formatter._extract_domain("https://www.example.com/page")
        assert result == "example.com"

    def test_recognizes_known_domains(self):
        """Recognizes and preserves known domains."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        known_urls = [
            ("https://arxiv.org/abs/1234", "arxiv.org"),
            ("https://github.com/user/repo", "github.com"),
            ("https://reddit.com/r/test", "reddit.com"),
            ("https://youtube.com/watch", "youtube.com"),
            ("https://pypi.org/project/test", "pypi.org"),
            ("https://milvus.io/docs", "milvus.io"),
            ("https://medium.com/article", "medium.com"),
        ]
        for url, expected in known_urls:
            result = formatter._extract_domain(url)
            assert result == expected, f"Failed for {url}"

    def test_handles_subdomain(self):
        """Handles subdomain in URL."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        result = formatter._extract_domain("https://docs.python.org/page")
        assert result == "python.org"

    def test_handles_invalid_url(self):
        """Returns 'source' for invalid URL."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        result = formatter._extract_domain("not-a-valid-url")
        # Should return something sensible, not crash
        assert isinstance(result, str)

    def test_handles_empty_url(self):
        """Handles empty URL string."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        result = formatter._extract_domain("")
        assert isinstance(result, str)


class TestCitationFormatterFindSourcesSection:
    """Tests for _find_sources_section method."""

    def test_finds_sources_header(self):
        """Finds ## Sources header."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content\n\n## Sources\n\n[1] Source one"
        result = formatter._find_sources_section(content)
        assert result != -1
        assert content[result:].startswith("## Sources")

    def test_finds_references_header(self):
        """Finds ## References header."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content\n\n## References\n\n[1] Ref one"
        result = formatter._find_sources_section(content)
        assert result != -1

    def test_finds_bibliography_header(self):
        """Finds ## Bibliography header."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content\n\n## Bibliography\n\n[1] Bib one"
        result = formatter._find_sources_section(content)
        assert result != -1

    def test_finds_citations_header(self):
        """Finds ## Citations header."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content\n\n## Citations\n\n[1] Citation one"
        result = formatter._find_sources_section(content)
        assert result != -1

    def test_case_insensitive(self):
        """Finds sources header case-insensitively."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content\n\n## SOURCES\n\n[1] Source one"
        result = formatter._find_sources_section(content)
        assert result != -1

    def test_returns_minus_one_when_not_found(self):
        """Returns -1 when no sources section found."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content without sources section."
        result = formatter._find_sources_section(content)
        assert result == -1

    def test_finds_single_hash_header(self):
        """Finds # Sources header."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content\n\n# Sources\n\n[1] Source one"
        result = formatter._find_sources_section(content)
        assert result != -1

    def test_finds_triple_hash_header(self):
        """Finds ### Sources header."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Some content\n\n### Sources\n\n[1] Source one"
        result = formatter._find_sources_section(content)
        assert result != -1


class TestCitationFormatterParseSources:
    """Tests for _parse_sources method."""

    def test_parses_single_source(self):
        """Parses a single source entry."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        sources_content = "[1] Test Source Title\n   URL: https://example.com"
        result = formatter._parse_sources(sources_content)
        assert "1" in result
        assert result["1"][0] == "Test Source Title"
        assert result["1"][1] == "https://example.com"

    def test_parses_multiple_sources(self):
        """Parses multiple source entries."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        sources_content = """[1] First Source
   URL: https://first.com
[2] Second Source
   URL: https://second.com"""
        result = formatter._parse_sources(sources_content)
        assert "1" in result
        assert "2" in result

    def test_parses_source_without_url(self):
        """Parses source without URL."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        sources_content = "[1] Source Without URL"
        result = formatter._parse_sources(sources_content)
        assert "1" in result
        assert result["1"][1] == ""  # Empty URL

    def test_parses_comma_separated_citation_numbers(self):
        """Parses comma-separated citation numbers like [36, 3]."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        sources_content = (
            "[36, 3] Shared Source Title\n   URL: https://shared.com"
        )
        result = formatter._parse_sources(sources_content)
        assert "36" in result
        assert "3" in result
        assert result["36"][0] == "Shared Source Title"
        assert result["3"][0] == "Shared Source Title"

    def test_returns_empty_dict_for_empty_content(self):
        """Returns empty dict for empty content."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        result = formatter._parse_sources("")
        assert result == {}


class TestCitationFormatterFormatDocument:
    """Tests for format_document method."""

    def test_returns_unchanged_for_no_hyperlinks_mode(self):
        """Returns unchanged content in NO_HYPERLINKS mode."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.NO_HYPERLINKS)
        content = "Text with [1] citation.\n\n## Sources\n\n[1] Source\n   URL: https://test.com"
        result = formatter.format_document(content)
        assert result == content

    def test_returns_unchanged_when_no_sources_section(self):
        """Returns unchanged when no sources section found."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1] citation but no sources section."
        result = formatter.format_document(content)
        assert result == content

    def test_formats_number_hyperlinks(self):
        """Formats citations with number hyperlinks."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.NUMBER_HYPERLINKS)
        content = "See [1] for details.\n\n## Sources\n\n[1] Test Source\n   URL: https://test.com"
        result = formatter.format_document(content)
        assert "[[1]](https://test.com)" in result

    def test_formats_domain_hyperlinks(self):
        """Formats citations with domain hyperlinks."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS)
        content = "See [1] for details.\n\n## Sources\n\n[1] Test Source\n   URL: https://example.com/page"
        result = formatter.format_document(content)
        assert "example.com" in result

    def test_preserves_sources_section(self):
        """Preserves the sources section unchanged."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        sources_section = (
            "## Sources\n\n[1] Test Source\n   URL: https://test.com"
        )
        content = f"See [1] here.\n\n{sources_section}"
        result = formatter.format_document(content)
        assert sources_section in result


class TestCitationFormatterFormatNumberHyperlinks:
    """Tests for _format_number_hyperlinks method."""

    def test_formats_single_citation(self):
        """Formats a single citation with number hyperlink."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1] here."
        sources = {"1": ("Title", "https://example.com")}
        result = formatter._format_number_hyperlinks(content, sources)
        assert "[[1]](https://example.com)" in result

    def test_formats_comma_separated_citations(self):
        """Formats comma-separated citations."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1, 2] here."
        sources = {
            "1": ("Title 1", "https://one.com"),
            "2": ("Title 2", "https://two.com"),
        }
        result = formatter._format_number_hyperlinks(content, sources)
        assert "[[1]](https://one.com)" in result
        assert "[[2]](https://two.com)" in result

    def test_formats_source_word_pattern(self):
        """Formats 'Source X' patterns."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "According to Source 1, this is true."
        sources = {"1": ("Title", "https://example.com")}
        result = formatter._format_number_hyperlinks(content, sources)
        assert "[[1]](https://example.com)" in result

    def test_leaves_citation_without_url_unchanged(self):
        """Leaves citation without URL unchanged."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1] here."
        sources = {"1": ("Title", "")}  # No URL
        result = formatter._format_number_hyperlinks(content, sources)
        assert "[1]" in result
        # Should not have hyperlink
        assert "[[1]](" not in result


class TestCitationFormatterFormatDomainHyperlinks:
    """Tests for _format_domain_hyperlinks method."""

    def test_replaces_number_with_domain(self):
        """Replaces citation number with domain."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1] here."
        sources = {"1": ("Title", "https://github.com/repo")}
        result = formatter._format_domain_hyperlinks(content, sources)
        assert "github.com" in result

    def test_handles_comma_separated_citations(self):
        """Handles comma-separated citations with domains."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1, 2] here."
        sources = {
            "1": ("Title 1", "https://github.com"),
            "2": ("Title 2", "https://arxiv.org"),
        }
        result = formatter._format_domain_hyperlinks(content, sources)
        assert "github.com" in result
        assert "arxiv.org" in result


class TestCitationFormatterFormatDomainIdHyperlinks:
    """Tests for _format_domain_id_hyperlinks method."""

    def test_no_id_for_single_domain_citation(self):
        """No ID suffix when only one citation from domain."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1] here."
        sources = {"1": ("Title", "https://github.com/repo")}
        result = formatter._format_domain_id_hyperlinks(content, sources)
        assert "[[github.com]](" in result
        assert "-1" not in result.split("github.com")[1].split("]")[0]

    def test_adds_id_for_multiple_domain_citations(self):
        """Adds ID suffix when multiple citations from same domain."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1] and [2] here."
        sources = {
            "1": ("Title 1", "https://github.com/repo1"),
            "2": ("Title 2", "https://github.com/repo2"),
        }
        result = formatter._format_domain_id_hyperlinks(content, sources)
        assert "github.com-1" in result
        assert "github.com-2" in result


class TestCitationFormatterFormatDomainIdAlwaysHyperlinks:
    """Tests for _format_domain_id_always_hyperlinks method."""

    def test_always_adds_id(self):
        """Always adds ID suffix even for single citation."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with [1] here."
        sources = {"1": ("Title", "https://github.com/repo")}
        result = formatter._format_domain_id_always_hyperlinks(content, sources)
        assert "github.com-1" in result


class TestCitationFormatterUnicodeBrackets:
    """Tests for handling Unicode lenticular brackets."""

    def test_matches_unicode_lenticular_brackets(self):
        """Matches Unicode lenticular brackets 【】."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Text with 【1】 here."
        sources = {"1": ("Title", "https://example.com")}
        result = formatter._format_number_hyperlinks(content, sources)
        assert "[[1]](https://example.com)" in result


class TestQuartoExporterInit:
    """Tests for QuartoExporter initialization."""

    def test_can_instantiate(self):
        """Can instantiate QuartoExporter."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        assert exporter is not None

    def test_has_citation_pattern(self):
        """Has citation pattern regex."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        assert exporter.citation_pattern is not None

    def test_has_comma_citation_pattern(self):
        """Has comma citation pattern regex."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        assert exporter.comma_citation_pattern is not None


class TestQuartoExporterExportToQuarto:
    """Tests for export_to_quarto method."""

    def test_adds_yaml_header(self):
        """Adds YAML front matter header."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "# My Report\n\nSome content."
        result = exporter.export_to_quarto(content)
        assert result.startswith("---")
        assert "title:" in result

    def test_uses_provided_title(self):
        """Uses provided title in YAML header."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "Some content."
        result = exporter.export_to_quarto(content, title="Custom Title")
        assert "Custom Title" in result

    def test_extracts_title_from_content(self):
        """Extracts title from content if not provided."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "# My Research Report\n\nSome content."
        result = exporter.export_to_quarto(content)
        assert "My Research Report" in result

    def test_converts_citations_to_quarto_format(self):
        """Converts [1] citations to [@ref1] format."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "See [1] for details."
        result = exporter.export_to_quarto(content)
        assert "[@ref1]" in result

    def test_converts_comma_separated_citations(self):
        """Converts comma-separated citations to Quarto format."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "See [1, 2, 3] for details."
        result = exporter.export_to_quarto(content)
        assert "@ref1" in result
        assert "@ref2" in result
        assert "@ref3" in result

    def test_adds_bibliography_note(self):
        """Adds bibliography file note."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = (
            "Content.\n\n## Sources\n\n[1] Source One\n   URL: https://test.com"
        )
        result = exporter.export_to_quarto(content)
        assert "references.bib" in result


class TestQuartoExporterGenerateBibliography:
    """Tests for _generate_bibliography method."""

    def test_generates_bibtex_entries(self):
        """Generates BibTeX entries from sources."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "[1] Test Source Title\n   URL: https://example.com"
        result = exporter._generate_bibliography(content)
        assert "@misc{ref1" in result
        # BibTeX uses braces and quotes: title = "{Title}"
        assert "Test Source Title" in result
        assert "title" in result

    def test_includes_url_in_bibtex(self):
        """Includes URL in BibTeX entry."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "[1] Test Source Title\n   URL: https://example.com"
        result = exporter._generate_bibliography(content)
        assert "url = {https://example.com}" in result

    def test_generates_multiple_entries(self):
        """Generates multiple BibTeX entries."""
        from local_deep_research.text_optimization.citation_formatter import (
            QuartoExporter,
        )

        exporter = QuartoExporter()
        content = "[1] First Source\n   URL: https://first.com\n[2] Second Source\n   URL: https://second.com"
        result = exporter._generate_bibliography(content)
        assert "@misc{ref1" in result
        assert "@misc{ref2" in result


class TestRISExporterInit:
    """Tests for RISExporter initialization."""

    def test_can_instantiate(self):
        """Can instantiate RISExporter."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        assert exporter is not None

    def test_has_sources_pattern(self):
        """Has sources pattern regex."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        assert exporter.sources_pattern is not None


class TestRISExporterExportToRIS:
    """Tests for export_to_ris method."""

    def test_returns_empty_for_no_sources(self):
        """Returns empty string when no sources section."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        content = "Content without sources section."
        result = exporter.export_to_ris(content)
        assert result == ""

    def test_finds_sources_section(self):
        """Finds ## Sources section."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        content = "Content.\n\n## Sources\n\n[1] Test Source\n   URL: https://test.com"
        result = exporter.export_to_ris(content)
        assert "TY  - ELEC" in result

    def test_finds_references_section(self):
        """Finds ## References section."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        content = "Content.\n\n## References\n\n[1] Test Ref\n   URL: https://test.com"
        result = exporter.export_to_ris(content)
        assert "TY  - ELEC" in result

    def test_includes_title(self):
        """Includes title in RIS entry."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        content = "## Sources\n\n[1] My Test Title"
        result = exporter.export_to_ris(content)
        assert "TI  -" in result

    def test_includes_url(self):
        """Includes URL in RIS entry."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        content = "## Sources\n\n[1] Test Source\nURL: https://example.com"
        result = exporter.export_to_ris(content)
        assert "UR  - https://example.com" in result

    def test_includes_end_of_reference(self):
        """Includes ER - end of reference marker."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        content = "## Sources\n\n[1] Test Source"
        result = exporter.export_to_ris(content)
        assert "ER  -" in result

    def test_includes_language(self):
        """Includes language field in RIS."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        content = "## Sources\n\n[1] Test Source"
        result = exporter.export_to_ris(content)
        assert "LA  - en" in result


class TestRISExporterCreateRISEntry:
    """Tests for _create_ris_entry method."""

    def test_creates_entry_with_id(self):
        """Creates RIS entry with reference ID."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        result = exporter._create_ris_entry("1", "Test Title")
        assert "ID  - ref1" in result

    def test_extracts_year_from_text(self):
        """Extracts year from text if present."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        result = exporter._create_ris_entry("1", "Test Title published in 2023")
        assert "PY  - 2023" in result

    def test_extracts_doi_from_metadata(self):
        """Extracts DOI from metadata."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        result = exporter._create_ris_entry(
            "1", "Test Title", metadata={"doi": "10.1234/test"}
        )
        assert "DO  - 10.1234/test" in result

    def test_extracts_publisher_from_github_url(self):
        """Extracts GitHub as publisher from URL."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        result = exporter._create_ris_entry(
            "1", "Test Title", url="https://github.com/user/repo"
        )
        assert "PB  - GitHub" in result

    def test_extracts_publisher_from_arxiv_url(self):
        """Extracts arXiv as publisher from URL."""
        from local_deep_research.text_optimization.citation_formatter import (
            RISExporter,
        )

        exporter = RISExporter()
        result = exporter._create_ris_entry(
            "1", "Test Title", url="https://arxiv.org/abs/1234"
        )
        assert "PB  - arXiv" in result


class TestLaTeXExporterInit:
    """Tests for LaTeXExporter initialization."""

    def test_can_instantiate(self):
        """Can instantiate LaTeXExporter."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        assert exporter is not None

    def test_has_citation_pattern(self):
        """Has citation pattern regex."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        assert exporter.citation_pattern is not None

    def test_has_heading_patterns(self):
        """Has heading patterns list."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        assert len(exporter.heading_patterns) > 0

    def test_has_emphasis_patterns(self):
        """Has emphasis patterns list."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        assert len(exporter.emphasis_patterns) > 0


class TestLaTeXExporterExportToLatex:
    """Tests for export_to_latex method."""

    def test_adds_document_class(self):
        """Adds documentclass declaration."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("Content")
        assert "\\documentclass" in result

    def test_adds_begin_document(self):
        """Adds begin{document}."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("Content")
        assert "\\begin{document}" in result

    def test_adds_end_document(self):
        """Adds end{document}."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("Content")
        assert "\\end{document}" in result

    def test_converts_h1_to_section(self):
        """Converts # heading to \\section."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("# My Heading\n\nContent")
        assert "\\section{My Heading}" in result

    def test_converts_h2_to_subsection(self):
        """Converts ## heading to \\subsection."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("## My Subheading\n\nContent")
        assert "\\subsection{My Subheading}" in result

    def test_converts_h3_to_subsubsection(self):
        """Converts ### heading to \\subsubsection."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("### My Sub-Subheading\n\nContent")
        assert "\\subsubsection{My Sub-Subheading}" in result

    def test_converts_bold_to_textbf(self):
        """Converts **bold** to \\textbf."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("This is **bold** text.")
        assert "\\textbf{bold}" in result

    def test_converts_italic_to_textit(self):
        """Converts *italic* to \\textit."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("This is *italic* text.")
        assert "\\textit{italic}" in result

    def test_converts_code_to_texttt(self):
        """Converts `code` to \\texttt."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("This is `code` text.")
        assert "\\texttt{code}" in result

    def test_converts_citations_to_cite(self):
        """Converts [1] to \\cite{1}."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter.export_to_latex("See [1] for details.")
        assert "\\cite{1}" in result

    def test_adds_bibliography(self):
        """Adds bibliography section when sources exist."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        content = "Content.\n\n## Sources\n\n[1] Test Source\n   URL: https://test.com"
        result = exporter.export_to_latex(content)
        assert "\\begin{thebibliography}" in result
        assert "\\end{thebibliography}" in result


class TestLaTeXExporterConvertLists:
    """Tests for _convert_lists method."""

    def test_converts_bullet_to_item(self):
        """Converts - bullet to \\item."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._convert_lists("- First item\n- Second item")
        assert "\\item First item" in result
        assert "\\item Second item" in result

    def test_adds_itemize_environment(self):
        """Adds itemize environment around list."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._convert_lists("- First item\n- Second item")
        assert "\\begin{itemize}" in result
        assert "\\end{itemize}" in result


class TestLaTeXExporterEscapeLatex:
    """Tests for _escape_latex method."""

    def test_escapes_ampersand(self):
        """Escapes & character."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("A & B")
        assert "\\&" in result

    def test_escapes_percent(self):
        """Escapes % character."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("50%")
        assert "\\%" in result

    def test_escapes_dollar(self):
        """Escapes $ character."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("$100")
        assert "\\$" in result

    def test_escapes_hash(self):
        """Escapes # character."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("#1")
        assert "\\#" in result

    def test_escapes_underscore(self):
        """Escapes _ character."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("file_name")
        assert "\\_" in result

    def test_escapes_braces(self):
        """Escapes { and } characters."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("{test}")
        assert "\\{" in result
        assert "\\}" in result

    def test_escapes_tilde(self):
        """Escapes ~ character."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("~test")
        assert "\\textasciitilde{}" in result

    def test_escapes_caret(self):
        """Escapes ^ character."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._escape_latex("x^2")
        assert "\\textasciicircum{}" in result


class TestLaTeXExporterCreateBibliography:
    """Tests for _create_bibliography method."""

    def test_returns_empty_for_no_sources(self):
        """Returns empty string when no sources section."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        result = exporter._create_bibliography("Content without sources.")
        assert result == ""

    def test_creates_bibitem_entries(self):
        """Creates bibitem entries for sources."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        content = "## Sources\n\n[1] Test Source Title"
        result = exporter._create_bibliography(content)
        assert "\\bibitem{1}" in result

    def test_includes_url_in_bibitem(self):
        """Includes URL in bibitem entry."""
        from local_deep_research.text_optimization.citation_formatter import (
            LaTeXExporter,
        )

        exporter = LaTeXExporter()
        content = (
            "## Sources\n\n[1] Test Source Title\n   URL: https://example.com"
        )
        result = exporter._create_bibliography(content)
        assert "\\url{https://example.com}" in result


class TestCitationFormatterFormatDocumentSplit:
    """Tests for format_document_split — answer / sources tuple return."""

    def test_returns_tuple_with_answer_and_sources(self):
        """Happy path: returns hyperlinked answer and raw sources block."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.NUMBER_HYPERLINKS)
        content = (
            "See [1] for details.\n\n"
            "## Sources\n\n[1] Test Source\n   URL: https://test.com"
        )
        answer, sources = formatter.format_document_split(content)
        assert "[[1]](https://test.com)" in answer
        assert sources.startswith("## Sources")
        assert "Test Source" in sources

    def test_returns_empty_sources_when_no_sources_section(self):
        """No Sources section in input → returns (content, '')."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.NUMBER_HYPERLINKS)
        content = "Just an answer with [1] but no Sources section."
        answer, sources = formatter.format_document_split(content)
        assert answer == content
        assert sources == ""

    def test_returns_unchanged_in_no_hyperlinks_mode(self):
        """NO_HYPERLINKS mode short-circuits to (content, '')."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.NO_HYPERLINKS)
        content = (
            "Text with [1].\n\n## Sources\n\n[1] Source\n   URL: https://x.com"
        )
        answer, sources = formatter.format_document_split(content)
        assert answer == content
        assert sources == ""

    def test_format_document_compat_equals_concatenation(self):
        """format_document() == answer + sources from format_document_split."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.NUMBER_HYPERLINKS)
        content = (
            "Body with [1] cite.\n\n"
            "## Sources\n\n[1] One\n   URL: https://one.com"
        )
        full = formatter.format_document(content)
        answer, sources = formatter.format_document_split(content)
        assert full == answer + sources


class TestCitationFormatterApplyInlineHyperlinks:
    """Tests for apply_inline_hyperlinks — structured-source delegation."""

    def test_replaces_bracket_refs_with_markdown_links(self):
        """[1] [2] become [[1]](url) [[2]](url) given a structured list."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "See [1] and [2]."
        sources = [
            {"index": "1", "title": "First", "url": "https://a.com"},
            {"index": "2", "title": "Second", "url": "https://b.com"},
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        assert "[[1]](https://a.com)" in result
        assert "[[2]](https://b.com)" in result

    def test_handles_missing_index_gracefully(self):
        """[7] left unchanged when no source with index 7."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "See [1] and [7]."
        sources = [
            {"index": "1", "title": "Only", "url": "https://a.com"},
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        assert "[[1]](https://a.com)" in result
        # [7] should remain as-is — formatter passes through unknown indices
        assert "[7]" in result

    def test_handles_comma_lists(self):
        """[1, 2] becomes individual links via comma_citation_pattern."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Per [1, 2] this is true."
        sources = [
            {"index": "1", "title": "First", "url": "https://a.com"},
            {"index": "2", "title": "Second", "url": "https://b.com"},
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        # _format_number_hyperlinks expands comma lists into individual links
        assert "https://a.com" in result
        assert "https://b.com" in result

    def test_skips_when_no_bracket_refs(self):
        """Content without [N] refs returns unchanged."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Plain text with no citations."
        sources = [
            {"index": "1", "title": "Unused", "url": "https://x.com"},
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        assert result == content

    def test_returns_content_unchanged_when_sources_empty(self):
        """Empty sources list short-circuits to content."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "See [1]."
        assert formatter.apply_inline_hyperlinks(content, []) == content

    def test_returns_empty_string_when_content_empty(self):
        """Empty content returns empty string, no crash."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        sources = [{"index": "1", "title": "x", "url": "https://x.com"}]
        assert formatter.apply_inline_hyperlinks("", sources) == ""
        assert formatter.apply_inline_hyperlinks(None, sources) == ""

    def test_skips_sources_with_missing_url(self):
        """Sources missing url are filtered out before delegation."""
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "See [1] and [2]."
        sources = [
            {"index": "1", "title": "Has URL", "url": "https://a.com"},
            {"index": "2", "title": "No URL"},  # missing url key
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        assert "[[1]](https://a.com)" in result
        # [2] left unchanged because that source was filtered
        assert "[2]" in result

    def test_accepts_link_key_for_searxng_compatibility(self):
        """Regression: Searxng-sourced results use 'link' not 'url'.

        search_engine_searxng.py emits ``{"link": url, "title": ...,
        "snippet": ...}``. The langgraph-agent strategy's collector stores
        these dicts verbatim in ``all_links_of_system`` and the chat-mode
        fallback path (``apply_inline_hyperlinks``) consumed them. Looking
        up only ``s["url"]`` silently dropped every Searxng-sourced
        citation — the answer body shipped with plain ``[N]`` brackets
        even though the Sources section beneath was fully populated.
        Both keys must be accepted.
        """
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "Per [1] and [2], yes."
        sources = [
            # Searxng shape
            {"index": "1", "title": "Searxng result", "link": "https://sx.com"},
            # Other-engine shape (still works)
            {"index": "2", "title": "Other engine", "url": "https://o.com"},
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        assert "[[1]](https://sx.com)" in result, (
            "Searxng-style 'link' key must produce a hyperlink"
        )
        assert "[[2]](https://o.com)" in result

    def test_prefers_url_over_link_when_both_present(self):
        """When a source carries both keys, ``url`` wins.

        The langgraph collector copies the raw engine dict and additionally
        sets ``r['link'] = r['url']`` when only ``url`` is present; other
        callers may set both for legacy reasons. The canonical destination
        is ``url`` — fall back to ``link`` only when ``url`` is missing.
        """
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
        )

        formatter = CitationFormatter()
        content = "See [1]."
        sources = [
            {
                "index": "1",
                "title": "Both",
                "url": "https://canonical.com",
                "link": "https://legacy.com",
            }
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        assert "[[1]](https://canonical.com)" in result

    def test_dispatches_on_self_mode_for_domain_hyperlinks(self):
        """Regression: ``apply_inline_hyperlinks`` was hard-coded to
        the NUMBER_HYPERLINKS formatter, so the chat-mode fallback
        path ignored ``report.citation_format``. A user who picked
        DOMAIN_HYPERLINKS in settings still got ``[[1]](url)`` on
        every chat answer instead of ``[[arxiv.org]](url)``.
        """
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS)
        content = "Per [1] and [2]."
        sources = [
            {"index": "1", "title": "T", "url": "https://arxiv.org/abs/123"},
            {"index": "2", "title": "T", "url": "https://nytimes.com/a"},
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        assert "[[arxiv.org]](https://arxiv.org/abs/123)" in result
        assert "[[nytimes.com]](https://nytimes.com/a)" in result
        assert "[[1]](" not in result  # must NOT fall back to NUMBER mode

    def test_dispatches_on_self_mode_for_domain_id_hyperlinks(self):
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.DOMAIN_ID_HYPERLINKS)
        content = "See [1] and [2]."
        sources = [
            {"index": "1", "title": "T", "url": "https://arxiv.org/abs/1"},
            {"index": "2", "title": "T", "url": "https://arxiv.org/abs/2"},
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        # Smart domain numbering: same domain twice → [arxiv.org-1] /
        # [arxiv.org-2] (or similar — the exact suffix is the formatter's
        # business; we assert it does NOT collapse to plain [N] mode).
        assert "arxiv.org" in result
        assert "[[1]](" not in result

    def test_dispatches_on_self_mode_for_source_tagged_hyperlinks(self):
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(
            mode=CitationMode.SOURCE_TAGGED_HYPERLINKS
        )
        content = "Per [1] and [2]."
        sources = [
            {"index": "1", "title": "T", "url": "https://arxiv.org/abs/1"},
            # Library hit: collection_name in metadata should win over
            # the URL-derived domain tag.
            {
                "index": "2",
                "title": "T",
                "url": "https://example.com/doc",
                "metadata": {"collection_name": "mypapers"},
            },
        ]
        result = formatter.apply_inline_hyperlinks(content, sources)
        # arxiv URL → [arxiv-1] (URLClassifier tag), collection → [mypapers-2].
        assert "arxiv-1" in result
        assert "mypapers-2" in result

    def test_no_hyperlinks_mode_returns_content_unchanged(self):
        from local_deep_research.text_optimization.citation_formatter import (
            CitationFormatter,
            CitationMode,
        )

        formatter = CitationFormatter(mode=CitationMode.NO_HYPERLINKS)
        content = "Per [1] and [2]."
        sources = [
            {"index": "1", "title": "T", "url": "https://a.com"},
            {"index": "2", "title": "T", "url": "https://b.com"},
        ]
        assert formatter.apply_inline_hyperlinks(content, sources) == content
