"""
Tests for the PubChem search engine.
Tests initialization, search functionality, and error handling.
"""

import pytest
from unittest.mock import Mock


class TestPubChemSearchEngineInit:
    """Tests for PubChem search engine initialization."""

    def test_init_default_parameters(self):
        """Test initialization with default parameters."""
        from local_deep_research.web_search_engines.engines.search_engine_pubchem import (
            PubChemSearchEngine,
        )

        engine = PubChemSearchEngine()

        assert engine.max_results == 10
        assert engine.include_synonyms is True
        assert engine.is_public is True
        assert engine.is_scientific is True

    def test_init_custom_parameters(self):
        """Test initialization with custom parameters."""
        from local_deep_research.web_search_engines.engines.search_engine_pubchem import (
            PubChemSearchEngine,
        )

        engine = PubChemSearchEngine(
            max_results=20,
            include_synonyms=False,
        )

        assert engine.max_results == 20
        assert engine.include_synonyms is False

    def test_base_url_set(self):
        """Test that API base URL is correctly set."""
        from local_deep_research.web_search_engines.engines.search_engine_pubchem import (
            PubChemSearchEngine,
        )

        engine = PubChemSearchEngine()
        assert engine.base_url == "https://pubchem.ncbi.nlm.nih.gov/rest/pug"
        assert (
            engine.autocomplete_url
            == "https://pubchem.ncbi.nlm.nih.gov/rest/autocomplete"
        )

    def test_user_agent_header_set(self):
        """Test that User-Agent header is set."""
        from local_deep_research.web_search_engines.engines.search_engine_pubchem import (
            PubChemSearchEngine,
        )

        engine = PubChemSearchEngine()
        assert "User-Agent" in engine.headers
        assert "Local-Deep-Research" in engine.headers["User-Agent"]


class TestPubChemSearchExecution:
    """Tests for PubChem search execution."""

    @pytest.fixture
    def engine(self):
        """Create a PubChem engine."""
        from local_deep_research.web_search_engines.engines.search_engine_pubchem import (
            PubChemSearchEngine,
        )

        return PubChemSearchEngine(max_results=5)

    def test_get_previews_handles_exception(self, engine, monkeypatch):
        """Test that exceptions are handled gracefully."""
        import requests

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            Mock(side_effect=requests.RequestException("Network error")),
        )

        previews = engine._get_previews("aspirin")
        assert previews == []


class TestPubChemErrorHandling:
    """Tests for PubChem error handling scenarios."""

    @pytest.fixture
    def engine(self):
        """Create a PubChem engine."""
        from local_deep_research.web_search_engines.engines.search_engine_pubchem import (
            PubChemSearchEngine,
        )

        return PubChemSearchEngine(max_results=5)

    def test_get_previews_handles_autocomplete_failure(
        self, engine, monkeypatch
    ):
        """Test that autocomplete returns results but direct lookup fails."""
        # Autocomplete returns a compound name, but the CID lookup fails
        mock_autocomplete = Mock()
        mock_autocomplete.status_code = 200
        mock_autocomplete.json = Mock(
            return_value={"dictionary_terms": {"compound": ["test compound"]}}
        )
        mock_autocomplete.raise_for_status = Mock()

        # CID lookup returns 404 (compound not found)
        mock_not_found = Mock()
        mock_not_found.status_code = 404

        def mock_safe_get(*args, **kwargs):
            if "autocomplete" in args[0]:
                return mock_autocomplete
            return mock_not_found

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            mock_safe_get,
        )

        # Should return empty list since compound lookup fails
        previews = engine._get_previews("test compound")
        assert previews == []

    def test_get_previews_handles_not_found_compound(self, engine, monkeypatch):
        """Test handling of 404 status code when compound is not found."""

        # First call for autocomplete returns empty (to trigger direct lookup path)
        mock_autocomplete = Mock()
        mock_autocomplete.status_code = 200
        mock_autocomplete.json = Mock(
            return_value={"dictionary_terms": {"compound": []}}
        )
        mock_autocomplete.raise_for_status = Mock()

        # Second call for direct lookup returns 404 (compound not found)
        mock_not_found = Mock()
        mock_not_found.status_code = 404

        call_count = [0]

        def mock_safe_get(*args, **kwargs):
            call_count[0] += 1
            if call_count[0] == 1:
                return mock_autocomplete
            return mock_not_found

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            mock_safe_get,
        )

        # Should return empty list since no compound found
        previews = engine._get_previews("nonexistent")
        assert previews == []

    def test_get_previews_raises_rate_limit_on_429(self, engine, monkeypatch):
        """Test that 429 status code raises RateLimitError."""
        from local_deep_research.web_search_engines.rate_limiting import (
            RateLimitError,
        )

        mock_response = Mock()
        mock_response.status_code = 429

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            Mock(return_value=mock_response),
        )

        with pytest.raises(RateLimitError):
            engine._get_previews("aspirin")

    def test_get_previews_empty_autocomplete_results(self, engine, monkeypatch):
        """Test handling when autocomplete returns no results."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json = Mock(
            return_value={"dictionary_terms": {"compound": []}}
        )
        mock_response.raise_for_status = Mock()

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("xyznonexistent12345")
        assert previews == []

    def test_get_previews_http_500_error(self, engine, monkeypatch):
        """Test handling of HTTP 500 server error."""
        import requests

        mock_response = Mock()
        mock_response.status_code = 500
        mock_response.raise_for_status = Mock(
            side_effect=requests.HTTPError("500 Server Error")
        )

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test")
        assert previews == []

    def test_get_previews_malformed_json(self, engine, monkeypatch):
        """Test handling of malformed JSON response."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json = Mock(side_effect=ValueError("Invalid JSON"))

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            Mock(return_value=mock_response),
        )

        previews = engine._get_previews("test")
        assert previews == []

    def test_compound_with_missing_properties(self, engine, monkeypatch):
        """Test handling of compound with missing properties."""
        # Mock for autocomplete
        mock_autocomplete = Mock()
        mock_autocomplete.status_code = 200
        mock_autocomplete.json = Mock(
            return_value={"dictionary_terms": {"compound": ["test compound"]}}
        )
        mock_autocomplete.raise_for_status = Mock()

        # Mock for compound CID lookup
        mock_compound = Mock()
        mock_compound.status_code = 200
        mock_compound.json = Mock(
            return_value={"IdentifierList": {"CID": [12345]}}
        )
        mock_compound.raise_for_status = Mock()

        # Mock for properties - empty
        mock_properties = Mock()
        mock_properties.status_code = 200
        mock_properties.json = Mock(
            return_value={"PropertyTable": {"Properties": [{}]}}
        )
        mock_properties.raise_for_status = Mock()

        # Mock for description - empty
        mock_description = Mock()
        mock_description.status_code = 200
        mock_description.json = Mock(
            return_value={"InformationList": {"Information": []}}
        )
        mock_description.raise_for_status = Mock()

        def mock_safe_get(*args, **kwargs):
            url = args[0]
            if "autocomplete" in url:
                return mock_autocomplete
            if "property" in url:
                return mock_properties
            if "description" in url:
                return mock_description
            return mock_compound

        monkeypatch.setattr(
            "local_deep_research.web_search_engines.engines.search_engine_pubchem.safe_get",
            mock_safe_get,
        )

        previews = engine._get_previews("test compound")
        # Should not crash, should return result with empty fields
        assert isinstance(previews, list)
        assert len(previews) == 1
        assert previews[0]["cid"] == 12345


class TestPubChemFullContent:
    """Tests for PubChem full content retrieval."""

    def test_get_full_content_builds_content(self):
        """Test that full content builds proper content string."""
        from local_deep_research.web_search_engines.engines.search_engine_pubchem import (
            PubChemSearchEngine,
        )

        engine = PubChemSearchEngine(include_synonyms=False)

        items = [
            {
                "id": "2244",
                "cid": 2244,
                "title": "Aspirin",
                "molecular_formula": "C9H8O4",
                "molecular_weight": "180.16",
                "iupac_name": "2-acetyloxybenzoic acid",
                "smiles": "CC(=O)OC1=CC=CC=C1C(=O)O",
                "inchi_key": "BSYNRYMUTXBXSQ-UHFFFAOYSA-N",
                "description": "Aspirin is an anti-inflammatory drug.",
                "_raw": {
                    "properties": {
                        "XLogP": 1.2,
                        "TPSA": 63.6,
                        "HBondDonorCount": 1,
                        "HBondAcceptorCount": 4,
                    },
                    "description": "Aspirin is an anti-inflammatory drug.",
                },
            }
        ]

        results = engine._get_full_content(items)

        assert len(results) == 1
        assert "Compound: Aspirin" in results[0]["content"]
        assert "CID: 2244" in results[0]["content"]
        assert "Molecular Formula: C9H8O4" in results[0]["content"]
        assert "Molecular Weight: 180.16 g/mol" in results[0]["content"]
        assert "_raw" not in results[0]
