"""Edge-case tests for research_library/services/pdf_storage_manager.py."""

import hashlib
from pathlib import Path
from unittest.mock import MagicMock, Mock, patch


def _make_manager(storage_mode="database", library_root="/tmp/test_lib"):
    """Create a PDFStorageManager for testing."""
    from local_deep_research.research_library.services.pdf_storage_manager import (
        PDFStorageManager,
    )

    return PDFStorageManager(
        library_root=Path(library_root),
        storage_mode=storage_mode,
        max_pdf_size_mb=100,
    )


class TestGenerateFilenameArxiv:
    """Tests for _generate_filename with arXiv URLs."""

    def test_generate_filename_arxiv_subdomain(self):
        """export.arxiv.org recognized via .endswith('.arxiv.org')."""
        manager = _make_manager()
        filename = manager._generate_filename(
            "https://export.arxiv.org/pdf/2301.12345", 1, "fallback.pdf"
        )
        assert "arxiv" in filename
        assert "2301.12345" in filename

    def test_generate_filename_arxiv_5digit_id(self):
        """Both 2301.1234 (4-digit) and 2401.12345 (5-digit) formats extracted."""
        manager = _make_manager()

        # 4-digit sub-ID
        fn4 = manager._generate_filename(
            "https://arxiv.org/abs/2301.1234", None, "fallback.pdf"
        )
        assert "2301.1234" in fn4

        # 5-digit sub-ID
        fn5 = manager._generate_filename(
            "https://arxiv.org/abs/2401.12345", None, "fallback.pdf"
        )
        assert "2401.12345" in fn5


class TestGenerateFilenamePMC:
    """Tests for _generate_filename with PMC URLs."""

    def test_generate_filename_pmc_exact_id(self):
        """PMC ID extracted precisely."""
        manager = _make_manager()
        filename = manager._generate_filename(
            "https://ncbi.nlm.nih.gov/pmc/articles/PMC7654321/",
            None,
            "fallback.pdf",
        )
        assert "PMC7654321" in filename
        assert filename.endswith(".pdf")


class TestHasPdfSentinel:
    """Tests for has_pdf sentinel checks."""

    def test_has_pdf_false_for_non_pdf_file_type(self):
        """has_pdf returns False for non-PDF file types before any DB query."""
        manager = _make_manager()
        document = Mock()
        document.file_type = "html"  # Not a PDF
        document.id = 1

        session = MagicMock()
        result = manager.has_pdf(document, session)
        assert result is False
        # No database query should have been made
        session.query.assert_not_called()

    def test_has_pdf_text_only_not_stored_is_sentinel(self):
        """Documents that 'text_only_not_stored' is treated as a sentinel in has_pdf.

        The source code checks `file_path not in ('metadata_only', 'text_only_not_stored')`
        to exclude sentinel values from filesystem lookup.

        Note: has_pdf currently has a bug — it references DocumentBlob.id but
        the model uses document_id as primary key. This test verifies the
        sentinel is listed in the exclusion check.
        """
        from local_deep_research.research_library.services.pdf_storage_manager import (
            PDFStorageManager,
        )
        import inspect

        source = inspect.getsource(PDFStorageManager.has_pdf)
        # Verify the sentinel value appears in the source code's exclusion check
        assert "text_only_not_stored" in source
        assert "metadata_only" in source


class TestInferStorageMode:
    """Tests for _infer_storage_mode edge cases."""

    def test_infer_storage_mode_text_only_not_stored(self):
        """Documents that _infer_storage_mode doesn't exclude 'text_only_not_stored'.

        The file_path 'text_only_not_stored' is not 'metadata_only', so
        _infer_storage_mode returns 'filesystem' even though there's no actual file.
        """
        manager = _make_manager()
        document = Mock()
        document.file_path = "text_only_not_stored"
        document.blob = None

        result = manager._infer_storage_mode(document)
        # This is actually a potential bug: text_only_not_stored is not metadata_only
        # so it falls through to 'filesystem' even though there's no file
        assert result == "filesystem"


class TestSaveToDatabase:
    """Tests for _save_to_database update vs create paths."""

    def test_save_to_database_updates_existing_blob(self):
        """Update path (line 254) — updating an existing blob."""
        manager = _make_manager()
        document = Mock()
        document.id = 42

        existing_blob = Mock()
        session = MagicMock()
        session.query.return_value.filter_by.return_value.first.return_value = (
            existing_blob
        )

        pdf_content = b"%PDF-1.4 test content"
        manager._save_to_database(pdf_content, document, session)

        # Verify the existing blob was updated, not a new one created
        assert existing_blob.pdf_binary == pdf_content
        assert (
            existing_blob.blob_hash == hashlib.sha256(pdf_content).hexdigest()
        )
        # session.add should NOT have been called (update, not create)
        session.add.assert_not_called()


class TestLoadFromDatabase:
    """Tests for _load_from_database side effects."""

    def test_load_from_database_updates_last_accessed(self):
        """last_accessed timestamp side-effect verified."""
        manager = _make_manager()
        document = Mock()
        document.id = 1

        blob = Mock()
        blob.pdf_binary = b"%PDF-1.4 content"
        blob.last_accessed = None

        session = MagicMock()
        session.query.return_value.filter_by.return_value.first.return_value = (
            blob
        )

        result = manager._load_from_database(document, session)

        assert result == b"%PDF-1.4 content"
        # last_accessed should have been updated
        assert blob.last_accessed is not None


class TestUpgradeToPdf:
    """Tests for upgrade_to_pdf edge cases."""

    def test_upgrade_to_pdf_with_none_storage_mode(self):
        """Legacy None storage_mode is accepted for upgrade."""
        manager = _make_manager()
        document = Mock()
        document.id = 10
        document.storage_mode = None
        document.file_path = None

        session = MagicMock()
        # No existing blob
        session.query.return_value.filter_by.return_value.first.return_value = (
            None
        )

        pdf_content = b"%PDF-1.4 small content"

        with patch.object(manager, "_save_to_database") as mock_save:
            result = manager.upgrade_to_pdf(document, pdf_content, session)

        assert result is True
        mock_save.assert_called_once_with(pdf_content, document, session)
        assert document.storage_mode == "database"

    def test_upgrade_to_pdf_with_string_none_rejected(self):
        """String 'none' storage_mode is also accepted for upgrade."""
        manager = _make_manager()
        document = Mock()
        document.id = 11
        document.storage_mode = "none"
        document.file_path = None

        session = MagicMock()
        session.query.return_value.filter_by.return_value.first.return_value = (
            None
        )

        pdf_content = b"%PDF-1.4 small content"

        with patch.object(manager, "_save_to_database") as mock_save:
            result = manager.upgrade_to_pdf(document, pdf_content, session)

        # "none" is in (None, "none"), so upgrade proceeds
        assert result is True
        mock_save.assert_called_once()
