"""Tests for ODTExporter using pypandoc."""

import pytest
from unittest.mock import patch

# Check if pandoc is available (required by pypandoc)
try:
    import pypandoc

    pypandoc.get_pandoc_version()
    PANDOC_AVAILABLE = True
except (ImportError, OSError):
    PANDOC_AVAILABLE = False

pytestmark = pytest.mark.skipif(
    not PANDOC_AVAILABLE,
    reason="Pandoc is not installed (required for ODT export)",
)


class TestODTExporterProperties:
    """Tests for ODTExporter properties."""

    def test_format_name_is_odt(self):
        """Test that format_name is 'odt'."""
        from local_deep_research.exporters.odt_exporter import ODTExporter

        exporter = ODTExporter()

        assert exporter.format_name == "odt"

    def test_file_extension_is_odt(self):
        """Test that file_extension is '.odt'."""
        from local_deep_research.exporters.odt_exporter import ODTExporter

        exporter = ODTExporter()

        assert exporter.file_extension == ".odt"

    def test_mimetype_is_correct(self):
        """Test that mimetype is correct for ODT."""
        from local_deep_research.exporters.odt_exporter import ODTExporter

        exporter = ODTExporter()

        assert exporter.mimetype == "application/vnd.oasis.opendocument.text"


class TestODTExporterExport:
    """Tests for ODTExporter.export method."""

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

        return ODTExporter()

    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_odt(self, exporter, simple_markdown):
        """Test that result is a valid ODT file (ZIP archive)."""
        result = exporter.export(simple_markdown)

        # ODT files are ZIP archives, they start with PK
        assert result.content[:2] == b"PK"

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

        # A simple ODT should be at least a few KB
        assert len(result.content) > 1000

    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(".odt")

    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)

        assert "research_report" in result.filename
        assert result.filename.endswith(".odt")

    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/vnd.oasis.opendocument.text"

    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_headings(self, exporter, sample_markdown):
        """Test handling of markdown with multiple heading levels."""
        result = exporter.export(sample_markdown)

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

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

        # Should create valid ODT
        assert result.content[:2] == b"PK"

    def test_handles_markdown_with_code_blocks(self, exporter, sample_markdown):
        """Test handling of markdown with code blocks."""
        result = exporter.export(sample_markdown)

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

    def test_handles_markdown_with_lists(self, exporter, sample_markdown):
        """Test handling of markdown with 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_nested_lists(self, exporter, markdown_with_nested_lists):
        """Test handling of nested lists."""
        result = exporter.export(markdown_with_nested_lists)

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

    def test_handles_links(self, exporter, markdown_with_links):
        """Test handling of markdown with links."""
        result = exporter.export(markdown_with_links)

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

    def test_handles_large_markdown(self, exporter):
        """Test handling of large markdown content."""
        # Create a large markdown document
        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"
        # Large content should produce larger ODT (Pandoc compresses efficiently)
        assert len(result.content) > 5000

    def test_includes_ldr_attribution(self, exporter, simple_markdown):
        """Test that LDR attribution is included."""
        # We can't easily check the content inside ODT without parsing it,
        # but we verify the export succeeds
        result = exporter.export(simple_markdown)

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

    def test_logs_odt_size(self, exporter, simple_markdown):
        """Test that ODT size is logged."""
        with patch(
            "local_deep_research.exporters.odt_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 ODT" in call_args
            assert "bytes" in call_args


class TestODTExporterErrorHandling:
    """Tests for error handling in ODTExporter."""

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

        return ODTExporter()

    def test_logs_exception_on_error(self, exporter):
        """Test that exceptions are logged when pandoc fails."""
        import subprocess

        with patch(
            "local_deep_research.exporters.odt_exporter.subprocess.run",
            side_effect=subprocess.CalledProcessError(
                1, "pandoc", stderr=b"Test error"
            ),
        ):
            with patch(
                "local_deep_research.exporters.odt_exporter.logger"
            ) as mock_logger:
                with pytest.raises(RuntimeError):
                    exporter.export("test")

                mock_logger.exception.assert_called_once()


class TestODTExporterIntegration:
    """Integration tests for ODTExporter with ExporterRegistry."""

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

        assert ExporterRegistry.is_format_supported("odt")

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

        exporter = ExporterRegistry.get_exporter("odt")

        assert isinstance(exporter, ODTExporter)

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

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

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


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

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

        return ODTExporter()

    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 be truncated to 50 chars + extension
        filename_without_ext = result.filename.rsplit(".", 1)[0]
        assert len(filename_without_ext) == 50
        assert result.filename.endswith(".odt")

    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(".odt")


class TestODTExporterMetadataVariations:
    """Tests for various metadata combinations."""

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

        return ODTExporter()

    def test_title_only(self, exporter, simple_markdown):
        """Test export with only title metadata."""
        from local_deep_research.exporters import ExportOptions

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

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

    def test_author_only(self, exporter, simple_markdown):
        """Test export with only author metadata."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(metadata={"author": "Test Author"})
        result = exporter.export(simple_markdown, options)

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

    def test_date_only(self, exporter, simple_markdown):
        """Test export with only date metadata."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(metadata={"date": "2024-01-15"})
        result = exporter.export(simple_markdown, options)

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

    def test_all_metadata(self, exporter, simple_markdown):
        """Test export with all metadata fields."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(
            title="Full Test Document",
            metadata={"author": "Test Author", "date": "2024-01-15"},
        )
        result = exporter.export(simple_markdown, options)

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

    def test_unicode_in_title(self, exporter, simple_markdown):
        """Test export with unicode characters in title."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(title="Café naïve 日本語")
        result = exporter.export(simple_markdown, options)

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

    def test_unicode_in_author(self, exporter, simple_markdown):
        """Test export with unicode characters in author."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(metadata={"author": "José García 山田太郎"})
        result = exporter.export(simple_markdown, options)

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


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

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

        return ODTExporter()

    def test_raises_error_for_oversized_content(self, exporter):
        """Test that ValueError is raised for content exceeding size limit."""
        from local_deep_research.exporters.odt_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 (doesn't raise)."""
        from local_deep_research.exporters.odt_exporter import MAX_CONTENT_SIZE
        import zipfile
        import io

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

        # Create a minimal valid ODT file for mocking
        odt_buffer = io.BytesIO()
        with zipfile.ZipFile(odt_buffer, "w") as zf:
            zf.writestr("mimetype", "application/vnd.oasis.opendocument.text")
            zf.writestr("content.xml", "<office:document-content/>")
        mock_odt_bytes = odt_buffer.getvalue()

        # Mock subprocess.run to avoid actually processing 50MB
        mock_result = type(
            "Result", (), {"stdout": mock_odt_bytes, "stderr": b""}
        )()

        with patch(
            "local_deep_research.exporters.odt_exporter.subprocess.run",
            return_value=mock_result,
        ):
            # This should not raise ValueError for size limit
            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 TestODTExporterMetadataSanitization:
    """Tests for metadata sanitization to prevent injection."""

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

        return ODTExporter()

    def test_sanitizes_title_with_dashes(self, exporter, simple_markdown):
        """Test that double dashes are removed from title."""
        from local_deep_research.exporters import ExportOptions

        # Title with potential argument injection pattern
        options = ExportOptions(title="Title --extract-media=/tmp")
        result = exporter.export(simple_markdown, options)

        # Should succeed and produce valid ODT
        assert result.content[:2] == b"PK"

    def test_sanitizes_author_with_newlines(self, exporter, simple_markdown):
        """Test that newlines are removed from author metadata."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(metadata={"author": "Author\nWith\nNewlines"})
        result = exporter.export(simple_markdown, options)

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

    def test_sanitizes_date_with_special_chars(self, exporter, simple_markdown):
        """Test that date with injection patterns is sanitized."""
        from local_deep_research.exporters import ExportOptions

        options = ExportOptions(metadata={"date": "2024--01--15 --verbose"})
        result = exporter.export(simple_markdown, options)

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


class TestODTExporterInMemoryConversion:
    """Tests verifying ODT conversion happens entirely in memory."""

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

        return ODTExporter()

    def test_no_temp_files_used(self, exporter, simple_markdown):
        """Test that conversion uses subprocess stdin/stdout, not temp files."""
        import subprocess

        with patch(
            "local_deep_research.exporters.odt_exporter.subprocess.run",
            wraps=subprocess.run,
        ) as mock_run:
            exporter.export(simple_markdown)

            # Verify subprocess was called with stdin input and stdout capture
            call_kwargs = mock_run.call_args
            assert call_kwargs.kwargs.get("input") is not None
            assert call_kwargs.kwargs.get("capture_output") is True

            # Verify -o - (stdout) is in the command
            cmd = call_kwargs.args[0]
            assert "-o" in cmd
            assert "-" in cmd[cmd.index("-o") + 1]

    def test_pandoc_error_propagates_without_cleanup_needed(self, exporter):
        """Test that pandoc errors propagate cleanly (no temp cleanup needed)."""
        import subprocess

        with patch(
            "local_deep_research.exporters.odt_exporter.subprocess.run",
            side_effect=subprocess.CalledProcessError(
                1, "pandoc", stderr=b"Pandoc failed"
            ),
        ):
            with pytest.raises(RuntimeError, match="Pandoc conversion failed"):
                exporter.export("# Test content")


class TestODTExporterFooter:
    """Tests for LDR attribution footer in output."""

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

        return ODTExporter()

    def test_footer_content_present_in_output(self, exporter, simple_markdown):
        """Test that LDR attribution footer appears in the ODT output."""
        import io
        import zipfile

        result = exporter.export(simple_markdown)

        # ODT is a ZIP archive - extract content.xml
        with zipfile.ZipFile(io.BytesIO(result.content), "r") as zf:
            content_xml = zf.read("content.xml").decode("utf-8")

        # Verify footer text is present in the content
        assert "Local Deep Research" in content_xml
        assert "Open Source AI Research Assistant" in content_xml
