"""
Tests for LibraryService.
"""

from unittest.mock import Mock, patch, MagicMock


class TestLibraryServiceUrlDetection:
    """Tests for URL detection methods."""

    def test_is_arxiv_url_with_arxiv_domain(self):
        """Detects arxiv.org URLs correctly."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            # Valid arXiv URLs
            assert (
                service._is_arxiv_url("https://arxiv.org/abs/2301.00001")
                is True
            )
            assert (
                service._is_arxiv_url("https://arxiv.org/pdf/2301.00001.pdf")
                is True
            )
            assert (
                service._is_arxiv_url("http://arxiv.org/abs/1234.5678") is True
            )
            assert (
                service._is_arxiv_url("https://export.arxiv.org/abs/2301.00001")
                is True
            )

    def test_is_arxiv_url_with_non_arxiv_domain(self):
        """Rejects non-arXiv URLs."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            assert service._is_arxiv_url("https://google.com") is False
            assert (
                service._is_arxiv_url("https://pubmed.ncbi.nlm.nih.gov/12345")
                is False
            )
            assert (
                service._is_arxiv_url("https://example.com/arxiv.org") is False
            )

    def test_is_arxiv_url_with_invalid_url(self):
        """Handles invalid URLs gracefully."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            assert service._is_arxiv_url("not a url") is False
            assert service._is_arxiv_url("") is False

    def test_is_pubmed_url_with_pubmed_domain(self):
        """Detects PubMed URLs correctly."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            # Valid PubMed URLs
            assert (
                service._is_pubmed_url(
                    "https://pubmed.ncbi.nlm.nih.gov/12345678"
                )
                is True
            )
            assert (
                service._is_pubmed_url(
                    "https://ncbi.nlm.nih.gov/pmc/articles/PMC1234567"
                )
                is True
            )

    def test_is_pubmed_url_with_non_pubmed_domain(self):
        """Rejects non-PubMed URLs."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            assert (
                service._is_pubmed_url("https://arxiv.org/abs/2301.00001")
                is False
            )
            assert service._is_pubmed_url("https://google.com") is False


class TestLibraryServiceDomainExtraction:
    """Tests for domain extraction."""

    def test_extract_domain_from_url(self):
        """Extracts domain from URL correctly."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            assert (
                service._extract_domain("https://arxiv.org/abs/2301.00001")
                == "arxiv.org"
            )
            assert (
                service._extract_domain("https://pubmed.ncbi.nlm.nih.gov/12345")
                == "pubmed.ncbi.nlm.nih.gov"
            )
            assert (
                service._extract_domain("https://example.com/path")
                == "example.com"
            )

    def test_extract_domain_with_invalid_url(self):
        """Handles invalid URLs gracefully."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            assert service._extract_domain("not a url") == ""
            assert service._extract_domain("") == ""


class TestLibraryServiceUrlHash:
    """Tests for URL hashing."""

    def test_get_url_hash_normalizes_url(self):
        """URL hashing normalizes URLs before hashing."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            # Same URL with different protocols should produce same hash
            hash1 = service._get_url_hash("https://arxiv.org/abs/2301.00001")
            hash2 = service._get_url_hash("http://arxiv.org/abs/2301.00001")
            assert hash1 == hash2

    def test_get_url_hash_removes_www(self):
        """URL hashing removes www prefix."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            hash1 = service._get_url_hash("https://www.example.com/page")
            hash2 = service._get_url_hash("https://example.com/page")
            assert hash1 == hash2

    def test_get_url_hash_removes_trailing_slash(self):
        """URL hashing removes trailing slashes."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            hash1 = service._get_url_hash("https://example.com/page/")
            hash2 = service._get_url_hash("https://example.com/page")
            assert hash1 == hash2


class TestLibraryServiceToggleFavorite:
    """Tests for toggling document favorites."""

    def test_toggle_favorite_document_found(self, library_session, mocker):
        """Toggles favorite status when document exists."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        # Create a mock document
        mock_doc = Mock()
        mock_doc.favorite = False

        # Mock the session context
        mock_session_context = mocker.patch(
            "src.local_deep_research.research_library.services.library_service.get_user_db_session"
        )
        mock_session = MagicMock()
        mock_session.__enter__ = Mock(return_value=mock_session)
        mock_session.__exit__ = Mock(return_value=False)
        mock_session.query.return_value.get.return_value = mock_doc
        mock_session_context.return_value = mock_session

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            result = service.toggle_favorite("doc-123")

            # Should toggle to True
            assert mock_doc.favorite is True
            assert result is True

    def test_toggle_favorite_document_not_found(self, mocker):
        """Returns False when document doesn't exist."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        # Mock the session context
        mock_session_context = mocker.patch(
            "src.local_deep_research.research_library.services.library_service.get_user_db_session"
        )
        mock_session = MagicMock()
        mock_session.__enter__ = Mock(return_value=mock_session)
        mock_session.__exit__ = Mock(return_value=False)
        mock_session.query.return_value.get.return_value = None
        mock_session_context.return_value = mock_session

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            result = service.toggle_favorite("nonexistent-doc")
            assert result is False


class TestLibraryServiceDeleteDocument:
    """Tests for document deletion."""

    def test_delete_document_not_found(self, mocker):
        """Returns False when document doesn't exist."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        # Mock the session context
        mock_session_context = mocker.patch(
            "src.local_deep_research.research_library.services.library_service.get_user_db_session"
        )
        mock_session = MagicMock()
        mock_session.__enter__ = Mock(return_value=mock_session)
        mock_session.__exit__ = Mock(return_value=False)
        mock_session.query.return_value.get.return_value = None
        mock_session_context.return_value = mock_session

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            result = service.delete_document("nonexistent-doc")
            assert result is False


class TestLibraryServiceGetUniqueDomains:
    """Tests for getting unique domains."""

    def test_get_unique_domains_returns_list(self, mocker):
        """Returns a list of unique domains."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        # Mock the session context with sample data
        mock_session_context = mocker.patch(
            "src.local_deep_research.research_library.services.library_service.get_user_db_session"
        )
        mock_session = MagicMock()
        mock_session.__enter__ = Mock(return_value=mock_session)
        mock_session.__exit__ = Mock(return_value=False)

        # Return mock domain data
        mock_session.query.return_value.filter.return_value.all.return_value = [
            ("arxiv.org",),
            ("pubmed",),
            ("other",),
        ]
        mock_session_context.return_value = mock_session

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            result = service.get_unique_domains()

            assert isinstance(result, list)
            assert "arxiv.org" in result
            assert "pubmed" in result


class TestLibraryServiceGetAllCollections:
    """Tests for getting all collections."""

    def test_get_all_collections_returns_list(self, mocker):
        """Returns a list of collections with document counts."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        # Create mock collection
        mock_collection = Mock()
        mock_collection.id = "coll-123"
        mock_collection.name = "Test Collection"
        mock_collection.description = "A test collection"
        mock_collection.is_default = False

        # Mock the session context
        mock_session_context = mocker.patch(
            "src.local_deep_research.research_library.services.library_service.get_user_db_session"
        )
        mock_session = MagicMock()
        mock_session.__enter__ = Mock(return_value=mock_session)
        mock_session.__exit__ = Mock(return_value=False)

        # Mock query chain
        mock_query = Mock()
        mock_query.outerjoin.return_value = mock_query
        mock_query.group_by.return_value = mock_query
        mock_query.order_by.return_value = mock_query
        mock_query.all.return_value = [(mock_collection, 5)]
        mock_session.query.return_value = mock_query
        mock_session_context.return_value = mock_session

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            result = service.get_all_collections()

            assert isinstance(result, list)
            assert len(result) == 1
            assert result[0]["id"] == "coll-123"
            assert result[0]["name"] == "Test Collection"
            assert result[0]["document_count"] == 5


class TestLibraryServiceGetDocumentById:
    """Tests for getting document by ID."""

    def test_get_document_by_id_not_found(self, mocker):
        """Returns None when document not found."""
        from src.local_deep_research.research_library.services.library_service import (
            LibraryService,
        )

        # Mock the session context
        mock_session_context = mocker.patch(
            "src.local_deep_research.research_library.services.library_service.get_user_db_session"
        )
        mock_session = MagicMock()
        mock_session.__enter__ = Mock(return_value=mock_session)
        mock_session.__exit__ = Mock(return_value=False)

        # Mock query to return None
        mock_query = Mock()
        mock_query.outerjoin.return_value = mock_query
        mock_query.filter.return_value = mock_query
        mock_query.first.return_value = None
        mock_session.query.return_value = mock_query
        mock_session_context.return_value = mock_session

        with patch.object(
            LibraryService, "__init__", lambda self, username: None
        ):
            service = LibraryService.__new__(LibraryService)
            service.username = "test_user"

            result = service.get_document_by_id("nonexistent-doc")
            assert result is None
