"""
Extended Tests for PDF Service

Phase 19: Socket & Real-time Services - PDF Service Tests
Tests PDF generation and extraction functionality.
"""

import pytest
from datetime import datetime, UTC
from unittest.mock import patch, MagicMock


class TestPDFGeneration:
    """Tests for PDF generation functionality"""

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_from_markdown(self, mock_service_cls):
        """Test PDF generation from markdown"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 mock content"

        markdown = "# Test Title\n\nThis is a test paragraph."
        result = mock_service.markdown_to_pdf(markdown)

        assert result.startswith(b"%PDF")

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_with_images(self, mock_service_cls):
        """Test PDF generation with embedded images"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with images"

        markdown = """
# Report with Image

![Chart](data:image/png;base64,iVBORw0KGgo=)

This is a caption.
"""
        result = mock_service.markdown_to_pdf(markdown)

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_with_tables(self, mock_service_cls):
        """Test PDF generation with tables"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with tables"

        markdown = """
# Report with Table

| Column 1 | Column 2 |
|----------|----------|
| Value 1  | Value 2  |
| Value 3  | Value 4  |
"""
        result = mock_service.markdown_to_pdf(markdown)

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_with_code_blocks(self, mock_service_cls):
        """Test PDF generation with code blocks"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with code"

        markdown = """
# Code Example

```python
def hello():
    print("Hello, World!")
```
"""
        result = mock_service.markdown_to_pdf(markdown)

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_with_math(self, mock_service_cls):
        """Test PDF generation with math expressions"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with math"

        markdown = """
# Math Example

The quadratic formula is: $x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}$
"""
        result = mock_service.markdown_to_pdf(markdown)

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_unicode_content(self, mock_service_cls):
        """Test PDF generation with unicode content"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with unicode"

        markdown = """
# Unicode Test

Chinese: 中文测试
Japanese: 日本語テスト
Emoji: 🔬📊📈
"""
        result = mock_service.markdown_to_pdf(markdown)

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_large_document(self, mock_service_cls):
        """Test PDF generation for large documents"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 large document"

        # Generate large markdown content
        sections = [
            f"## Section {i}\n\nContent for section {i}.\n\n"
            for i in range(100)
        ]
        markdown = "# Large Document\n\n" + "".join(sections)

        result = mock_service.markdown_to_pdf(markdown)

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_page_layout(self, mock_service_cls):
        """Test PDF page layout settings"""
        mock_service = MagicMock()
        mock_service._get_page_settings.return_value = {
            "size": "A4",
            "margins": {
                "top": "1.5cm",
                "bottom": "1.5cm",
                "left": "1.5cm",
                "right": "1.5cm",
            },
        }

        settings = mock_service._get_page_settings()

        assert settings["size"] == "A4"

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_headers_footers(self, mock_service_cls):
        """Test PDF headers and footers"""
        mock_service = MagicMock()
        mock_service._add_headers_footers.return_value = True

        result = mock_service._add_headers_footers(
            "Test Report", {"page_numbers": True}
        )

        assert result is True

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_table_of_contents(self, mock_service_cls):
        """Test PDF table of contents generation"""
        mock_service = MagicMock()
        mock_service._generate_toc.return_value = """
## Table of Contents

1. [Section 1](#section-1)
2. [Section 2](#section-2)
"""

        toc = mock_service._generate_toc(["Section 1", "Section 2"])

        assert "Section 1" in toc

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_hyperlinks(self, mock_service_cls):
        """Test PDF hyperlink preservation"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with links"

        markdown = """
# Links Test

Visit [Example](https://example.com) for more information.
"""
        result = mock_service.markdown_to_pdf(markdown)

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_metadata(self, mock_service_cls):
        """Test PDF metadata embedding"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with metadata"

        result = mock_service.markdown_to_pdf(
            "# Test",
            metadata={
                "title": "Test Report",
                "author": "Test Author",
                "created": datetime.now(UTC).isoformat(),
            },
        )

        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_compression(self, mock_service_cls):
        """Test PDF compression"""
        mock_service = MagicMock()
        mock_service._compress_pdf.return_value = b"%PDF-1.4 compressed"

        original = b"%PDF-1.4 original content" * 1000
        compressed = mock_service._compress_pdf(original)

        # Compressed should be smaller or equal
        assert len(compressed) <= len(original)

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_error_recovery(self, mock_service_cls):
        """Test error recovery during PDF generation"""
        mock_service = MagicMock()
        mock_service.markdown_to_pdf.side_effect = [
            Exception("First attempt failed"),
            b"%PDF-1.4 success on retry",
        ]

        # First call fails
        with pytest.raises(Exception):
            mock_service.markdown_to_pdf("# Test")

        # Second call succeeds
        result = mock_service.markdown_to_pdf("# Test")
        assert result is not None

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_generate_pdf_timeout_handling(self, mock_service_cls):
        """Test timeout handling during generation"""
        mock_service = MagicMock()
        mock_service._generate_with_timeout.return_value = {
            "success": True,
            "pdf": b"%PDF-1.4",
        }

        result = mock_service._generate_with_timeout(
            "# Test", timeout_seconds=30
        )

        assert result["success"] is True


class TestPDFExtraction:
    """Tests for PDF extraction functionality"""

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_text_from_pdf(self, mock_service_cls):
        """Test text extraction from PDF"""
        mock_service = MagicMock()
        mock_service.extract_text_and_metadata.return_value = {
            "text": "Extracted text content",
            "pages": 5,
            "success": True,
        }

        result = mock_service.extract_text_and_metadata(
            b"%PDF-1.4 content", "test.pdf"
        )

        assert result["success"] is True
        assert len(result["text"]) > 0

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_text_corrupted_pdf(self, mock_service_cls):
        """Test handling corrupted PDF"""
        mock_service = MagicMock()
        mock_service.extract_text_and_metadata.return_value = {
            "text": "",
            "success": False,
            "error": "Invalid PDF format",
        }

        result = mock_service.extract_text_and_metadata(
            b"not a valid pdf", "corrupted.pdf"
        )

        assert result["success"] is False
        assert "error" in result

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_text_encrypted_pdf(self, mock_service_cls):
        """Test handling encrypted PDF"""
        mock_service = MagicMock()
        mock_service.extract_text_and_metadata.return_value = {
            "text": "",
            "success": False,
            "error": "PDF is encrypted",
        }

        result = mock_service.extract_text_and_metadata(
            b"%PDF-1.4 encrypted", "encrypted.pdf"
        )

        assert result["success"] is False

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_text_scanned_pdf(self, mock_service_cls):
        """Test handling scanned (image-based) PDF"""
        mock_service = MagicMock()
        mock_service.extract_text_and_metadata.return_value = {
            "text": "",
            "pages": 3,
            "success": True,
            "warning": "PDF appears to be image-based",
        }

        result = mock_service.extract_text_and_metadata(
            b"%PDF-1.4 scanned", "scanned.pdf"
        )

        # May succeed but with empty or minimal text
        assert result["success"] is True

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_metadata_from_pdf(self, mock_service_cls):
        """Test metadata extraction"""
        mock_service = MagicMock()
        mock_service.extract_text_and_metadata.return_value = {
            "text": "Content",
            "pages": 10,
            "size": 50000,
            "filename": "research.pdf",
            "success": True,
        }

        result = mock_service.extract_text_and_metadata(
            b"%PDF-1.4 content", "research.pdf"
        )

        assert result["pages"] == 10
        assert result["size"] == 50000

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_images_from_pdf(self, mock_service_cls):
        """Test image extraction from PDF"""
        mock_service = MagicMock()
        mock_service._extract_images.return_value = [
            {"page": 1, "image": b"image1"},
            {"page": 3, "image": b"image2"},
        ]

        images = mock_service._extract_images(b"%PDF-1.4 with images")

        assert len(images) == 2

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_tables_from_pdf(self, mock_service_cls):
        """Test table extraction from PDF"""
        mock_service = MagicMock()
        mock_service._extract_tables.return_value = [
            {"page": 1, "data": [["Header", "Value"], ["Row", "Data"]]}
        ]

        tables = mock_service._extract_tables(b"%PDF-1.4 with tables")

        assert len(tables) == 1

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_large_pdf_streaming(self, mock_service_cls):
        """Test streaming extraction for large PDFs"""
        mock_service = MagicMock()
        mock_service._extract_streaming.return_value = iter(
            [
                {"page": 1, "text": "Page 1 text"},
                {"page": 2, "text": "Page 2 text"},
            ]
        )

        pages = list(mock_service._extract_streaming(b"%PDF-1.4 large"))

        assert len(pages) == 2

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_pdf_page_selection(self, mock_service_cls):
        """Test extracting specific pages"""
        mock_service = MagicMock()
        mock_service._extract_pages.return_value = {
            "text": "Pages 5-10 content",
            "pages_extracted": [5, 6, 7, 8, 9, 10],
        }

        result = mock_service._extract_pages(
            b"%PDF-1.4 content", start_page=5, end_page=10
        )

        assert len(result["pages_extracted"]) == 6

    @patch(
        "local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
    )
    def test_extract_pdf_timeout_handling(self, mock_service_cls):
        """Test extraction timeout handling"""
        mock_service = MagicMock()
        mock_service.extract_text_and_metadata.return_value = {
            "text": "",
            "success": False,
            "error": "Extraction timeout",
        }

        result = mock_service.extract_text_and_metadata(
            b"%PDF-1.4 large complex pdf", "huge.pdf"
        )

        # May fail due to timeout
        assert "error" in result or result["success"] is True


class TestHTMLToMarkdown:
    """Tests for HTML to markdown conversion"""

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_markdown_to_html_conversion(self, mock_service_cls):
        """Test markdown to HTML conversion"""
        mock_service = MagicMock()
        mock_service._markdown_to_html.return_value = (
            "<h1>Title</h1><p>Content</p>"
        )

        html = mock_service._markdown_to_html("# Title\n\nContent")

        assert "<h1>" in html

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_markdown_extensions(self, mock_service_cls):
        """Test markdown extensions are applied"""
        mock_service = MagicMock()
        mock_service._get_markdown_extensions.return_value = [
            "tables",
            "fenced_code",
            "footnotes",
            "toc",
        ]

        extensions = mock_service._get_markdown_extensions()

        assert "tables" in extensions
        assert "fenced_code" in extensions


class TestCSSGeneration:
    """Tests for CSS generation"""

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_default_css_generation(self, mock_service_cls):
        """Test default CSS is generated"""
        mock_service = MagicMock()
        mock_service._get_default_css.return_value = """
@page { size: A4; margin: 1.5cm; }
body { font-family: Arial; font-size: 10pt; }
"""

        css = mock_service._get_default_css()

        assert "@page" in css
        assert "A4" in css

    @patch("local_deep_research.web.services.pdf_service.PDFService")
    def test_custom_css_application(self, mock_service_cls):
        """Test custom CSS is applied"""
        mock_service = MagicMock()
        mock_service._apply_custom_css.return_value = """
body { font-size: 12pt; color: #333; }
"""

        custom_css = "body { font-size: 12pt; color: #333; }"
        result = mock_service._apply_custom_css(custom_css)

        assert "12pt" in result


class TestSingleton:
    """Tests for PDF service singleton"""

    def test_get_pdf_service_returns_instance(self):
        """Test get_pdf_service returns an instance"""
        from local_deep_research.web.services.pdf_service import get_pdf_service

        service = get_pdf_service()

        assert service is not None

    def test_get_pdf_service_singleton(self):
        """Test get_pdf_service returns 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
