"""
Comprehensive tests for PDFService.
Tests PDF generation, markdown conversion, CSS handling, and metadata.
"""

import pytest
from unittest.mock import patch


class TestPDFServiceInit:
    """Tests for PDFService initialization."""

    def test_init_creates_minimal_css(self):
        """Test that initialization creates minimal CSS."""
        from local_deep_research.web.services.pdf_service import PDFService

        service = PDFService()

        assert service.minimal_css is not None

    def test_minimal_css_includes_page_settings(self):
        """Test that minimal CSS includes @page settings."""
        from local_deep_research.web.services.pdf_service import PDFService

        service = PDFService()

        # The CSS object wraps the string, so we check it was created
        assert service.minimal_css is not None


class TestPDFServiceMarkdownToHtml:
    """Tests for _markdown_to_html method."""

    @pytest.fixture
    def service(self):
        """Create PDFService instance."""
        from local_deep_research.web.services.pdf_service import PDFService

        return PDFService()

    def test_converts_basic_markdown(self, service, simple_markdown):
        """Test conversion of basic markdown to HTML."""
        html = service._markdown_to_html(simple_markdown)

        # Markdown adds id attribute to headings
        assert "<h1" in html and "Hello World</h1>" in html
        assert "<p>This is a test.</p>" in html

    def test_includes_doctype_and_structure(self, service, simple_markdown):
        """Test that HTML includes proper structure."""
        html = service._markdown_to_html(simple_markdown)

        assert "<!DOCTYPE html>" in html
        assert "<html>" in html
        assert "</html>" in html
        assert "<head>" in html
        assert "</head>" in html
        assert "<body>" in html
        assert "</body>" in html

    def test_includes_charset_meta(self, service, simple_markdown):
        """Test that HTML includes UTF-8 charset."""
        html = service._markdown_to_html(simple_markdown)

        assert '<meta charset="utf-8">' in html

    def test_includes_title_when_provided(self, service, simple_markdown):
        """Test that title is included when provided."""
        html = service._markdown_to_html(simple_markdown, title="Test Title")

        assert "<title>Test Title</title>" in html

    def test_excludes_title_when_not_provided(self, service, simple_markdown):
        """Test that no title tag when title not provided."""
        html = service._markdown_to_html(simple_markdown)

        assert "<title>" not in html

    def test_includes_metadata_as_meta_tags(
        self, service, simple_markdown, sample_metadata
    ):
        """Test that metadata is included as meta tags."""
        html = service._markdown_to_html(
            simple_markdown, metadata=sample_metadata
        )

        assert '<meta name="author" content="Test Author">' in html
        assert '<meta name="date" content="2024-01-15">' in html
        assert '<meta name="subject" content="Test Subject">' in html

    def test_includes_ldr_attribution_footer(self, service, simple_markdown):
        """Test that LDR attribution footer is included."""
        html = service._markdown_to_html(simple_markdown)

        assert "Generated by" in html
        assert "Local Deep Research" in html

    def test_converts_tables(self, service, sample_markdown):
        """Test that markdown tables are converted."""
        html = service._markdown_to_html(sample_markdown)

        assert "<table>" in html
        assert "<th>" in html
        assert "Column 1" in html

    def test_converts_code_blocks(self, service, sample_markdown):
        """Test that fenced code blocks are converted."""
        html = service._markdown_to_html(sample_markdown)

        assert "<code" in html or "<pre>" in html
        assert "def example():" in html

    def test_converts_lists(self, service, sample_markdown):
        """Test that lists are converted."""
        html = service._markdown_to_html(sample_markdown)

        assert "<li>" in html
        assert "Item 1" in html

    def test_converts_bold_and_italic(self, service, sample_markdown):
        """Test that bold and italic text are converted."""
        html = service._markdown_to_html(sample_markdown)

        assert "<strong>bold</strong>" in html
        assert "<em>italic</em>" in html

    def test_handles_special_characters(
        self, service, markdown_with_special_chars
    ):
        """Test handling of special characters."""
        html = service._markdown_to_html(markdown_with_special_chars)

        # Unicode should pass through
        assert "café" in html
        assert "naïve" in html
        assert "日本語" in html


class TestPDFServiceMarkdownToPdf:
    """Tests for markdown_to_pdf method."""

    @pytest.fixture
    def service(self):
        """Create PDFService instance."""
        from local_deep_research.web.services.pdf_service import PDFService

        return PDFService()

    def test_returns_bytes(self, service, simple_markdown):
        """Test that markdown_to_pdf returns bytes."""
        result = service.markdown_to_pdf(simple_markdown)

        assert isinstance(result, bytes)

    def test_returns_valid_pdf(self, service, simple_markdown):
        """Test that result is a valid PDF (starts with %PDF)."""
        result = service.markdown_to_pdf(simple_markdown)

        assert result.startswith(b"%PDF")

    def test_pdf_has_reasonable_size(self, service, simple_markdown):
        """Test that generated PDF has a reasonable size."""
        result = service.markdown_to_pdf(simple_markdown)

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

    def test_applies_title(self, service, simple_markdown):
        """Test that title is applied to PDF."""
        result = service.markdown_to_pdf(simple_markdown, title="My Title")

        # PDF was generated successfully
        assert result.startswith(b"%PDF")
        assert len(result) > 0

    def test_applies_metadata(self, service, simple_markdown, sample_metadata):
        """Test that metadata is applied to PDF."""
        result = service.markdown_to_pdf(
            simple_markdown, metadata=sample_metadata
        )

        assert result.startswith(b"%PDF")

    def test_applies_custom_css(self, service, simple_markdown, custom_css):
        """Test that custom CSS is applied."""
        result = service.markdown_to_pdf(simple_markdown, custom_css=custom_css)

        assert result.startswith(b"%PDF")

    def test_uses_minimal_css_when_no_custom_css(
        self, service, simple_markdown
    ):
        """Test that minimal CSS is used by default."""
        result = service.markdown_to_pdf(simple_markdown)

        assert result.startswith(b"%PDF")

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

        assert result.startswith(b"%PDF")

    def test_handles_large_markdown(self, service):
        """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 = service.markdown_to_pdf(large_content)

        assert result.startswith(b"%PDF")
        # Large content should produce larger PDF
        assert len(result) > 10000

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

        assert result.startswith(b"%PDF")

    def test_logs_pdf_size(self, service, simple_markdown):
        """Test that PDF size is logged."""
        with patch(
            "local_deep_research.web.services.pdf_service.logger"
        ) as mock_logger:
            service.markdown_to_pdf(simple_markdown)

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


class TestPDFServiceErrorHandling:
    """Tests for error handling in PDFService."""

    @pytest.fixture
    def service(self):
        """Create PDFService instance."""
        from local_deep_research.web.services.pdf_service import PDFService

        return PDFService()

    def test_raises_exception_on_html_error(self, service):
        """Test that exceptions are raised on HTML conversion errors."""
        with patch.object(
            service, "_markdown_to_html", side_effect=Exception("HTML error")
        ):
            with pytest.raises(Exception) as exc_info:
                service.markdown_to_pdf("test")

            assert "HTML error" in str(exc_info.value)

    def test_logs_exception_on_error(self, service):
        """Test that exceptions are logged."""
        with patch.object(
            service, "_markdown_to_html", side_effect=Exception("Test error")
        ):
            with patch(
                "local_deep_research.web.services.pdf_service.logger"
            ) as mock_logger:
                with pytest.raises(Exception):
                    service.markdown_to_pdf("test")

                mock_logger.exception.assert_called_once()


class TestPDFServiceSingleton:
    """Tests for get_pdf_service singleton."""

    def test_get_pdf_service_returns_instance(self):
        """Test that get_pdf_service returns a PDFService instance."""
        from local_deep_research.web.services.pdf_service import (
            get_pdf_service,
            PDFService,
        )

        service = get_pdf_service()

        assert isinstance(service, PDFService)

    def test_get_pdf_service_returns_same_instance(self):
        """Test that get_pdf_service returns the same instance."""
        from local_deep_research.web.services.pdf_service import (
            get_pdf_service,
        )

        service1 = get_pdf_service()
        service2 = get_pdf_service()

        assert service1 is service2

    def test_singleton_can_be_reset(self):
        """Test that singleton can be reset for testing."""
        import local_deep_research.web.services.pdf_service as pdf_module

        # Reset singleton
        pdf_module._pdf_service = None

        service1 = pdf_module.get_pdf_service()

        # Reset again
        pdf_module._pdf_service = None

        service2 = pdf_module.get_pdf_service()

        # Different instances after reset
        assert service1 is not service2
