"""
Tests for web/services/pdf_extraction_service.py

Tests cover:
- PDFExtractionService.extract_text_and_metadata()
- PDFExtractionService.extract_batch()
- get_pdf_extraction_service() singleton
"""

from unittest.mock import Mock, patch, MagicMock


class TestExtractTextAndMetadata:
    """Tests for extract_text_and_metadata method."""

    def test_extract_text_and_metadata_success(self):
        """Test successful text extraction from PDF."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        mock_pdf_content = b"fake pdf content"

        with patch(
            "local_deep_research.web.services.pdf_extraction_service.pdfplumber"
        ) as mock_pdfplumber:
            mock_pdf = MagicMock()
            mock_page = MagicMock()
            mock_page.extract_text.return_value = "Extracted text from page 1"
            mock_pdf.pages = [mock_page]
            mock_pdf.__enter__ = Mock(return_value=mock_pdf)
            mock_pdf.__exit__ = Mock(return_value=False)
            mock_pdfplumber.open.return_value = mock_pdf

            result = PDFExtractionService.extract_text_and_metadata(
                mock_pdf_content, "test.pdf"
            )

            assert result["success"] is True
            assert result["text"] == "Extracted text from page 1"
            assert result["pages"] == 1
            assert result["filename"] == "test.pdf"
            assert result["size"] == len(mock_pdf_content)
            assert result["error"] is None

    def test_extract_text_and_metadata_multiple_pages(self):
        """Test extraction from multi-page PDF."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        mock_pdf_content = b"fake pdf content"

        with patch(
            "local_deep_research.web.services.pdf_extraction_service.pdfplumber"
        ) as mock_pdfplumber:
            mock_pdf = MagicMock()
            mock_page1 = MagicMock()
            mock_page1.extract_text.return_value = "Page 1 text"
            mock_page2 = MagicMock()
            mock_page2.extract_text.return_value = "Page 2 text"
            mock_page3 = MagicMock()
            mock_page3.extract_text.return_value = "Page 3 text"
            mock_pdf.pages = [mock_page1, mock_page2, mock_page3]
            mock_pdf.__enter__ = Mock(return_value=mock_pdf)
            mock_pdf.__exit__ = Mock(return_value=False)
            mock_pdfplumber.open.return_value = mock_pdf

            result = PDFExtractionService.extract_text_and_metadata(
                mock_pdf_content, "multipage.pdf"
            )

            assert result["success"] is True
            assert "Page 1 text" in result["text"]
            assert "Page 2 text" in result["text"]
            assert "Page 3 text" in result["text"]
            assert result["pages"] == 3

    def test_extract_text_and_metadata_no_text(self):
        """Test extraction when PDF has no extractable text."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        mock_pdf_content = b"fake pdf content"

        with patch(
            "local_deep_research.web.services.pdf_extraction_service.pdfplumber"
        ) as mock_pdfplumber:
            mock_pdf = MagicMock()
            mock_page = MagicMock()
            mock_page.extract_text.return_value = ""
            mock_pdf.pages = [mock_page]
            mock_pdf.__enter__ = Mock(return_value=mock_pdf)
            mock_pdf.__exit__ = Mock(return_value=False)
            mock_pdfplumber.open.return_value = mock_pdf

            result = PDFExtractionService.extract_text_and_metadata(
                mock_pdf_content, "empty.pdf"
            )

            assert result["success"] is False
            assert result["text"] == ""
            assert "No extractable text found" in result["error"]

    def test_extract_text_and_metadata_whitespace_only(self):
        """Test extraction when PDF has only whitespace."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        mock_pdf_content = b"fake pdf content"

        with patch(
            "local_deep_research.web.services.pdf_extraction_service.pdfplumber"
        ) as mock_pdfplumber:
            mock_pdf = MagicMock()
            mock_page = MagicMock()
            mock_page.extract_text.return_value = "   \n\t  "
            mock_pdf.pages = [mock_page]
            mock_pdf.__enter__ = Mock(return_value=mock_pdf)
            mock_pdf.__exit__ = Mock(return_value=False)
            mock_pdfplumber.open.return_value = mock_pdf

            result = PDFExtractionService.extract_text_and_metadata(
                mock_pdf_content, "whitespace.pdf"
            )

            assert result["success"] is False

    def test_extract_text_and_metadata_page_returns_none(self):
        """Test extraction when a page returns None."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        mock_pdf_content = b"fake pdf content"

        with patch(
            "local_deep_research.web.services.pdf_extraction_service.pdfplumber"
        ) as mock_pdfplumber:
            mock_pdf = MagicMock()
            mock_page1 = MagicMock()
            mock_page1.extract_text.return_value = "Page 1"
            mock_page2 = MagicMock()
            mock_page2.extract_text.return_value = None
            mock_pdf.pages = [mock_page1, mock_page2]
            mock_pdf.__enter__ = Mock(return_value=mock_pdf)
            mock_pdf.__exit__ = Mock(return_value=False)
            mock_pdfplumber.open.return_value = mock_pdf

            result = PDFExtractionService.extract_text_and_metadata(
                mock_pdf_content, "partial.pdf"
            )

            assert result["success"] is True
            assert result["text"] == "Page 1"
            assert result["pages"] == 2

    def test_extract_text_and_metadata_exception(self):
        """Test extraction when pdfplumber raises exception."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        mock_pdf_content = b"invalid pdf content"

        with patch(
            "local_deep_research.web.services.pdf_extraction_service.pdfplumber"
        ) as mock_pdfplumber:
            mock_pdfplumber.open.side_effect = Exception("Invalid PDF")

            result = PDFExtractionService.extract_text_and_metadata(
                mock_pdf_content, "invalid.pdf"
            )

            assert result["success"] is False
            assert result["text"] == ""
            assert result["pages"] == 0
            assert "Failed to extract text from PDF" in result["error"]

    def test_extract_text_and_metadata_strips_text(self):
        """Test that extracted text is stripped."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        mock_pdf_content = b"fake pdf content"

        with patch(
            "local_deep_research.web.services.pdf_extraction_service.pdfplumber"
        ) as mock_pdfplumber:
            mock_pdf = MagicMock()
            mock_page = MagicMock()
            mock_page.extract_text.return_value = "  Text with spaces  "
            mock_pdf.pages = [mock_page]
            mock_pdf.__enter__ = Mock(return_value=mock_pdf)
            mock_pdf.__exit__ = Mock(return_value=False)
            mock_pdfplumber.open.return_value = mock_pdf

            result = PDFExtractionService.extract_text_and_metadata(
                mock_pdf_content, "test.pdf"
            )

            assert result["text"] == "Text with spaces"


class TestExtractBatch:
    """Tests for extract_batch method."""

    def test_extract_batch_single_file_success(self):
        """Test batch extraction with single successful file."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        files_data = [{"content": b"pdf1", "filename": "file1.pdf"}]

        with patch.object(
            PDFExtractionService,
            "extract_text_and_metadata",
            return_value={
                "text": "Extracted",
                "pages": 1,
                "size": 4,
                "filename": "file1.pdf",
                "success": True,
                "error": None,
            },
        ):
            result = PDFExtractionService.extract_batch(files_data)

            assert result["total_files"] == 1
            assert result["successful"] == 1
            assert result["failed"] == 0
            assert len(result["results"]) == 1
            assert len(result["errors"]) == 0

    def test_extract_batch_multiple_files_success(self):
        """Test batch extraction with multiple successful files."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        files_data = [
            {"content": b"pdf1", "filename": "file1.pdf"},
            {"content": b"pdf2", "filename": "file2.pdf"},
            {"content": b"pdf3", "filename": "file3.pdf"},
        ]

        with patch.object(
            PDFExtractionService,
            "extract_text_and_metadata",
            return_value={
                "text": "Extracted",
                "pages": 1,
                "size": 4,
                "filename": "test.pdf",
                "success": True,
                "error": None,
            },
        ):
            result = PDFExtractionService.extract_batch(files_data)

            assert result["total_files"] == 3
            assert result["successful"] == 3
            assert result["failed"] == 0

    def test_extract_batch_with_failures(self):
        """Test batch extraction with some failures."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        files_data = [
            {"content": b"pdf1", "filename": "good.pdf"},
            {"content": b"pdf2", "filename": "bad.pdf"},
        ]

        def mock_extract(content, filename):
            if filename == "good.pdf":
                return {
                    "text": "Extracted",
                    "pages": 1,
                    "size": 4,
                    "filename": filename,
                    "success": True,
                    "error": None,
                }
            return {
                "text": "",
                "pages": 0,
                "size": 4,
                "filename": filename,
                "success": False,
                "error": "Failed to extract",
            }

        with patch.object(
            PDFExtractionService,
            "extract_text_and_metadata",
            side_effect=mock_extract,
        ):
            result = PDFExtractionService.extract_batch(files_data)

            assert result["total_files"] == 2
            assert result["successful"] == 1
            assert result["failed"] == 1
            assert len(result["errors"]) == 1
            assert "bad.pdf" in result["errors"][0]

    def test_extract_batch_empty_list(self):
        """Test batch extraction with empty list."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        result = PDFExtractionService.extract_batch([])

        assert result["total_files"] == 0
        assert result["successful"] == 0
        assert result["failed"] == 0
        assert len(result["results"]) == 0

    def test_extract_batch_all_failures(self):
        """Test batch extraction when all files fail."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        files_data = [
            {"content": b"pdf1", "filename": "fail1.pdf"},
            {"content": b"pdf2", "filename": "fail2.pdf"},
        ]

        with patch.object(
            PDFExtractionService,
            "extract_text_and_metadata",
            return_value={
                "text": "",
                "pages": 0,
                "size": 4,
                "filename": "fail.pdf",
                "success": False,
                "error": "Failed",
            },
        ):
            result = PDFExtractionService.extract_batch(files_data)

            assert result["total_files"] == 2
            assert result["successful"] == 0
            assert result["failed"] == 2
            assert len(result["errors"]) == 2


class TestGetPdfExtractionService:
    """Tests for get_pdf_extraction_service singleton."""

    def test_returns_pdf_extraction_service_instance(self):
        """Test that function returns PDFExtractionService instance."""
        from local_deep_research.web.services.pdf_extraction_service import (
            get_pdf_extraction_service,
            PDFExtractionService,
        )

        service = get_pdf_extraction_service()

        assert isinstance(service, PDFExtractionService)

    def test_returns_same_instance(self):
        """Test that function returns the same singleton instance."""
        from local_deep_research.web.services.pdf_extraction_service import (
            get_pdf_extraction_service,
        )

        service1 = get_pdf_extraction_service()
        service2 = get_pdf_extraction_service()

        assert service1 is service2


class TestPDFExtractionServiceClass:
    """Tests for PDFExtractionService class."""

    def test_class_has_static_methods(self):
        """Test that class has required static methods."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        assert hasattr(PDFExtractionService, "extract_text_and_metadata")
        assert hasattr(PDFExtractionService, "extract_batch")
        assert callable(PDFExtractionService.extract_text_and_metadata)
        assert callable(PDFExtractionService.extract_batch)

    def test_instance_can_be_created(self):
        """Test that PDFExtractionService can be instantiated."""
        from local_deep_research.web.services.pdf_extraction_service import (
            PDFExtractionService,
        )

        service = PDFExtractionService()

        assert service is not None
