"""
Tests for LibraryService domain detection and query filter methods.

Covers:
- _is_pubmed_url edge cases (ncbi without /pmc, substring in hostname, etc.)
- _apply_domain_filter generic substring match against original_url
- _apply_search_filter pattern wrapping and return values
"""

from unittest.mock import patch, MagicMock


# ============== Helper to create service ==============


def _make_service():
    from 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"
    return service


# ============== _is_pubmed_url edge cases ==============


class TestIsPubmedUrlEdgeCases:
    """Edge cases for _is_pubmed_url beyond the basic positive tests."""

    def test_ncbi_without_pmc_path_returns_false(self):
        """ncbi.nlm.nih.gov without /pmc in path should be False."""
        service = _make_service()
        assert (
            service._is_pubmed_url("https://ncbi.nlm.nih.gov/gene/12345")
            is False
        )

    def test_ncbi_pubchem_path_returns_false(self):
        """ncbi.nlm.nih.gov/pubchem (non-pmc sub-path) should be False."""
        service = _make_service()
        assert (
            service._is_pubmed_url("https://ncbi.nlm.nih.gov/pubchem/123")
            is False
        )

    def test_pubmed_substring_in_hostname_returns_true(self):
        """Hostname containing 'pubmed' as substring matches the catch-all."""
        service = _make_service()
        # Line 100: "pubmed" in hostname
        assert (
            service._is_pubmed_url("https://mypubmed.example.com/article")
            is True
        )

    def test_pubmed_only_in_path_returns_false(self):
        """'pubmed' appearing only in the path (not hostname) should be False."""
        service = _make_service()
        assert service._is_pubmed_url("https://example.com/pubmed/123") is False

    def test_empty_hostname_url_returns_false(self):
        """URL with no hostname should return False via the guard."""
        service = _make_service()
        assert service._is_pubmed_url("://nohost") is False

    def test_www_pubmed_subdomain_returns_true(self):
        """www.pubmed.ncbi.nlm.nih.gov should match via 'pubmed' in hostname."""
        service = _make_service()
        assert (
            service._is_pubmed_url("https://www.pubmed.ncbi.nlm.nih.gov/12345")
            is True
        )


# ============== _apply_domain_filter ==============


class TestApplyDomainFilter:
    """Tests for _apply_domain_filter generic substring match."""

    def test_filter_uses_like_with_domain_substring(self):
        """A plain domain is wrapped as %domain% with escape='\\\\'."""
        service = _make_service()
        mock_query = MagicMock()
        mock_model = MagicMock()

        result = service._apply_domain_filter(
            mock_query, mock_model, "nature.com"
        )

        mock_model.original_url.like.assert_called_once_with(
            "%nature.com%", escape="\\"
        )
        mock_query.filter.assert_called_once()
        assert result == mock_query.filter.return_value

    def test_filter_escapes_underscore_wildcard(self):
        """A domain with '_' has it escaped so it's not a SQL wildcard."""
        service = _make_service()
        mock_query = MagicMock()
        mock_model = MagicMock()

        service._apply_domain_filter(
            mock_query, mock_model, "my_journal.example.com"
        )

        mock_model.original_url.like.assert_called_once_with(
            "%my\\_journal.example.com%", escape="\\"
        )

    def test_filter_escapes_percent_wildcard(self):
        """A domain with '%' has it escaped so it's not a SQL wildcard."""
        service = _make_service()
        mock_query = MagicMock()
        mock_model = MagicMock()

        service._apply_domain_filter(mock_query, mock_model, "100%site.com")

        mock_model.original_url.like.assert_called_once_with(
            "%100\\%site.com%", escape="\\"
        )


# ============== _apply_search_filter ==============


class TestApplySearchFilter:
    """Tests for _apply_search_filter method."""

    def test_search_filter_wraps_query_in_wildcards(self, mocker):
        """Search query is wrapped as %search_query% with escape='\\\\'."""
        service = _make_service()
        mock_query = MagicMock()
        mock_model = MagicMock()

        mocker.patch(
            "local_deep_research.research_library.services.library_service.or_",
            return_value=MagicMock(),
        )
        mock_resource = MagicMock()
        mocker.patch(
            "local_deep_research.research_library.services.library_service.ResearchResource",
            mock_resource,
        )

        service._apply_search_filter(
            mock_query, mock_model, "quantum computing"
        )

        # Verify all 4 fields called with correct pattern + escape kwarg
        expected_pattern = "%quantum computing%"
        mock_model.title.ilike.assert_called_once_with(
            expected_pattern, escape="\\"
        )
        mock_model.authors.ilike.assert_called_once_with(
            expected_pattern, escape="\\"
        )
        mock_model.doi.ilike.assert_called_once_with(
            expected_pattern, escape="\\"
        )
        mock_resource.title.ilike.assert_called_once_with(
            expected_pattern, escape="\\"
        )

    def test_search_filter_uses_four_ilike_conditions(self, mocker):
        """Search filter creates or_() with 4 ilike conditions."""
        service = _make_service()
        mock_query = MagicMock()
        mock_model = MagicMock()

        mock_or = mocker.patch(
            "local_deep_research.research_library.services.library_service.or_",
            return_value=MagicMock(),
        )
        mock_resource = MagicMock()
        mocker.patch(
            "local_deep_research.research_library.services.library_service.ResearchResource",
            mock_resource,
        )

        service._apply_search_filter(mock_query, mock_model, "test")

        # or_() receives 4 arguments
        assert mock_or.call_count == 1
        args = mock_or.call_args[0]
        assert len(args) == 4

    def test_search_filter_escapes_sql_wildcards(self, mocker):
        """SQL special chars (% and _) in query are escaped, not wildcards."""
        service = _make_service()
        mock_query = MagicMock()
        mock_model = MagicMock()

        mocker.patch(
            "local_deep_research.research_library.services.library_service.or_",
            return_value=MagicMock(),
        )
        mocker.patch(
            "local_deep_research.research_library.services.library_service.ResearchResource",
            MagicMock(),
        )

        service._apply_search_filter(mock_query, mock_model, "100%_test")

        # Both '%' and '_' get backslash-escaped before being wrapped
        mock_model.title.ilike.assert_called_once_with(
            "%100\\%\\_test%", escape="\\"
        )

    def test_search_filter_returns_filtered_query(self, mocker):
        """Return value is the result of query.filter()."""
        service = _make_service()
        mock_query = MagicMock()
        mock_model = MagicMock()

        mocker.patch(
            "local_deep_research.research_library.services.library_service.or_",
            return_value=MagicMock(),
        )
        mocker.patch(
            "local_deep_research.research_library.services.library_service.ResearchResource",
            MagicMock(),
        )

        result = service._apply_search_filter(mock_query, mock_model, "test")

        assert result == mock_query.filter.return_value
