"""Tests for QuartoExporter."""

import io
import zipfile

import pytest
from unittest.mock import patch


class TestQuartoExporterProperties:
    """Tests for QuartoExporter properties."""

    def test_format_name_is_quarto(self):
        """Test that format_name is 'quarto'."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        exporter = QuartoExporter()

        assert exporter.format_name == "quarto"

    def test_file_extension_is_zip(self):
        """Test that file_extension is '.zip'."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        exporter = QuartoExporter()

        assert exporter.file_extension == ".zip"

    def test_mimetype_is_correct(self):
        """Test that mimetype is correct for Quarto (ZIP)."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        exporter = QuartoExporter()

        assert exporter.mimetype == "application/zip"


class TestQuartoExporterExport:
    """Tests for QuartoExporter.export method."""

    @pytest.fixture
    def exporter(self):
        """Create QuartoExporter instance."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        return QuartoExporter()

    def test_returns_export_result(self, exporter, simple_markdown):
        """Test that export returns ExportResult."""
        from local_deep_research.exporters import ExportResult

        result = exporter.export(simple_markdown)

        assert isinstance(result, ExportResult)

    def test_result_content_is_bytes(self, exporter, simple_markdown):
        """Test that result content is bytes."""
        result = exporter.export(simple_markdown)

        assert isinstance(result.content, bytes)

    def test_result_is_valid_zip(self, exporter, simple_markdown):
        """Test that result is a valid ZIP file."""
        result = exporter.export(simple_markdown)

        # ZIP files start with PK
        assert result.content[:2] == b"PK"

    def test_result_has_reasonable_size(self, exporter, simple_markdown):
        """Test that generated ZIP has a reasonable size."""
        result = exporter.export(simple_markdown)

        # A simple Quarto ZIP should have some content
        assert len(result.content) > 100

    def test_result_filename_uses_title(self, exporter, simple_markdown):
        """Test that filename uses provided title."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(title="My Research Report")
        result = exporter.export(simple_markdown, options)

        assert "My_Research_Report" in result.filename
        assert result.filename.endswith(".zip")

    def test_result_filename_default_when_no_title(
        self, exporter, simple_markdown
    ):
        """Test that filename uses default when no title."""
        result = exporter.export(simple_markdown)

        # Should use title from markdown or default
        assert result.filename.endswith(".zip")
        assert "_quarto.zip" in result.filename

    def test_result_mimetype_is_correct(self, exporter, simple_markdown):
        """Test that result mimetype is correct."""
        result = exporter.export(simple_markdown)

        assert result.mimetype == "application/zip"

    def test_handles_empty_markdown(self, exporter):
        """Test handling of empty markdown."""
        result = exporter.export("")

        assert result.content[:2] == b"PK"

    def test_handles_markdown_with_all_features(
        self, exporter, sample_markdown
    ):
        """Test handling of markdown with tables, code, lists."""
        result = exporter.export(sample_markdown)

        assert result.content[:2] == b"PK"

    def test_handles_special_characters(
        self, exporter, markdown_with_special_chars
    ):
        """Test handling of special characters."""
        result = exporter.export(markdown_with_special_chars)

        assert result.content[:2] == b"PK"

    def test_handles_large_markdown(self, exporter):
        """Test handling of large markdown content."""
        large_content = "# Large Document\n\n"
        large_content += ("This is a paragraph. " * 100 + "\n\n") * 50

        result = exporter.export(large_content)

        assert result.content[:2] == b"PK"
        assert len(result.content) > 500

    def test_logs_quarto_size(self, exporter, simple_markdown):
        """Test that Quarto size is logged."""
        with patch(
            "local_deep_research.exporters.quarto_exporter.logger"
        ) as mock_logger:
            exporter.export(simple_markdown)

            mock_logger.info.assert_called_once()
            call_args = mock_logger.info.call_args[0][0]
            assert "Generated Quarto" in call_args
            assert "bytes" in call_args


class TestQuartoExporterZipContents:
    """Tests for Quarto ZIP file contents."""

    @pytest.fixture
    def exporter(self):
        """Create QuartoExporter instance."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        return QuartoExporter()

    def test_zip_contains_qmd_file(self, exporter, simple_markdown):
        """Test that ZIP contains a .qmd file."""
        result = exporter.export(simple_markdown)

        with zipfile.ZipFile(io.BytesIO(result.content), "r") as zf:
            file_names = zf.namelist()

        qmd_files = [f for f in file_names if f.endswith(".qmd")]
        assert len(qmd_files) == 1

    def test_zip_contains_bib_file(self, exporter, simple_markdown):
        """Test that ZIP contains a references.bib file."""
        result = exporter.export(simple_markdown)

        with zipfile.ZipFile(io.BytesIO(result.content), "r") as zf:
            file_names = zf.namelist()

        assert "references.bib" in file_names

    def test_qmd_file_has_content(self, exporter, simple_markdown):
        """Test that .qmd file has content."""
        result = exporter.export(simple_markdown)

        with zipfile.ZipFile(io.BytesIO(result.content), "r") as zf:
            file_names = zf.namelist()
            qmd_files = [f for f in file_names if f.endswith(".qmd")]
            qmd_content = zf.read(qmd_files[0]).decode("utf-8")

        assert len(qmd_content) > 0

    def test_qmd_file_contains_yaml_header(self, exporter, sample_markdown):
        """Test that .qmd file contains YAML header."""
        result = exporter.export(sample_markdown)

        with zipfile.ZipFile(io.BytesIO(result.content), "r") as zf:
            file_names = zf.namelist()
            qmd_files = [f for f in file_names if f.endswith(".qmd")]
            qmd_content = zf.read(qmd_files[0]).decode("utf-8")

        # Quarto documents typically have YAML front matter
        assert "---" in qmd_content


class TestQuartoExporterContentSizeLimit:
    """Tests for content size limit enforcement."""

    @pytest.fixture
    def exporter(self):
        """Create QuartoExporter instance."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        return QuartoExporter()

    def test_raises_error_for_oversized_content(self, exporter):
        """Test that ValueError is raised for content exceeding size limit."""
        from local_deep_research.exporters.quarto_exporter import (
            MAX_CONTENT_SIZE,
        )

        # Create content that exceeds the limit
        oversized_content = "x" * (MAX_CONTENT_SIZE + 1)

        with pytest.raises(ValueError) as exc_info:
            exporter.export(oversized_content)

        assert "exceeds maximum size" in str(exc_info.value)

    def test_accepts_content_at_limit(self, exporter):
        """Test that content at exactly the limit is accepted."""
        from local_deep_research.exporters.quarto_exporter import (
            MAX_CONTENT_SIZE,
        )

        # Create content at exactly the limit
        content_at_limit = "x" * MAX_CONTENT_SIZE

        # Mock the legacy exporter to avoid processing large content
        with patch.object(
            exporter._legacy,
            "export_to_quarto",
            return_value="---\ntitle: Test\n---\n\nContent",
        ):
            with patch.object(
                exporter._legacy, "_generate_bibliography", return_value=""
            ):
                result = exporter.export(content_at_limit)
                assert result.content[:2] == b"PK"

    def test_accepts_content_under_limit(self, exporter, simple_markdown):
        """Test that content under the limit is accepted."""
        result = exporter.export(simple_markdown)

        assert result.content[:2] == b"PK"


class TestQuartoExporterErrorHandling:
    """Tests for error handling in QuartoExporter."""

    @pytest.fixture
    def exporter(self):
        """Create QuartoExporter instance."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        return QuartoExporter()

    def test_logs_exception_on_error(self, exporter):
        """Test that exceptions are logged."""
        with patch.object(
            exporter._legacy,
            "export_to_quarto",
            side_effect=Exception("Test error"),
        ):
            with patch(
                "local_deep_research.exporters.quarto_exporter.logger"
            ) as mock_logger:
                with pytest.raises(Exception):
                    exporter.export("test")

                mock_logger.exception.assert_called_once()


class TestQuartoExporterIntegration:
    """Integration tests for QuartoExporter with ExporterRegistry."""

    def test_registered_in_registry(self):
        """Test that QuartoExporter is registered in the registry."""
        from local_deep_research.exporters import ExporterRegistry

        assert ExporterRegistry.is_format_supported("quarto")

    def test_can_get_from_registry(self):
        """Test that QuartoExporter can be retrieved from registry."""
        from local_deep_research.exporters import ExporterRegistry
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        exporter = ExporterRegistry.get_exporter("quarto")

        assert isinstance(exporter, QuartoExporter)

    def test_export_via_registry(self, simple_markdown):
        """Test export via registry lookup."""
        from local_deep_research.exporters import ExporterRegistry

        exporter = ExporterRegistry.get_exporter("quarto")
        result = exporter.export(simple_markdown)

        assert result.content[:2] == b"PK"
        assert result.filename.endswith(".zip")


class TestQuartoExporterFilenameTruncation:
    """Tests for filename truncation with long titles."""

    @pytest.fixture
    def exporter(self):
        """Create QuartoExporter instance."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        return QuartoExporter()

    def test_filename_truncated_to_50_chars(self, exporter, simple_markdown):
        """Test that filename is truncated when title exceeds 50 chars."""
        from local_deep_research.exporters import ExportOptions

        # Title with more than 50 characters
        long_title = "A" * 60
        options = ExportOptions(title=long_title)
        result = exporter.export(simple_markdown, options)

        # Filename should have truncated base + _quarto.zip suffix
        assert result.filename.endswith("_quarto.zip")
        # The base (without _quarto.zip) should be 50 chars
        base_name = result.filename.replace("_quarto.zip", "")
        assert len(base_name) == 50

    def test_filename_not_truncated_under_50_chars(
        self, exporter, simple_markdown
    ):
        """Test that filename is not truncated when under 50 chars."""
        from local_deep_research.exporters import ExportOptions

        title = "Short Title"
        options = ExportOptions(title=title)
        result = exporter.export(simple_markdown, options)

        assert "Short_Title" in result.filename
        assert result.filename.endswith("_quarto.zip")


class TestQuartoExporterTitleExtraction:
    """Tests for title extraction from markdown."""

    @pytest.fixture
    def exporter(self):
        """Create QuartoExporter instance."""
        from local_deep_research.exporters.quarto_exporter import QuartoExporter

        return QuartoExporter()

    def test_extracts_title_for_yaml_header(self, exporter):
        """Test that title is extracted from markdown for YAML header.

        When no title is provided in options, the exporter extracts it from
        the first markdown heading for use in the YAML front matter.
        The filename uses the default "research_report" when no title option
        is provided.
        """
        markdown = "# My Document Title\n\nSome content here."

        result = exporter.export(markdown)

        # Filename uses default when no title in options
        assert "research_report" in result.filename
        assert result.filename.endswith("_quarto.zip")

        # But the YAML header inside the qmd should contain the extracted title
        with zipfile.ZipFile(io.BytesIO(result.content), "r") as zf:
            file_names = zf.namelist()
            qmd_files = [f for f in file_names if f.endswith(".qmd")]
            qmd_content = zf.read(qmd_files[0]).decode("utf-8")

        # The title should appear in the YAML front matter
        assert "My Document Title" in qmd_content

    def test_uses_default_title_when_no_heading(self, exporter):
        """Test that default title is used when no heading present."""
        markdown = "Just some content without a heading."

        result = exporter.export(markdown)

        # Should still create valid output
        assert result.content[:2] == b"PK"
        assert result.filename.endswith("_quarto.zip")
