from typing import Any, Dict, List, Optional

from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
from langchain_core.language_models import BaseLLM
from loguru import logger

from ..rate_limiting import RateLimitError
from ..search_engine_base import BaseSearchEngine


class DuckDuckGoSearchEngine(BaseSearchEngine):
    """DuckDuckGo search engine implementation with two-phase retrieval"""

    # Mark as public search engine
    is_public = True
    # Mark as generic search engine (general web search)
    is_generic = True

    def __init__(
        self,
        max_results: int = 10,
        region: str = "us",
        safe_search: bool = True,
        llm: Optional[BaseLLM] = None,
        language: str = "English",
        include_full_content: bool = False,
        max_filtered_results: Optional[int] = None,
        settings_snapshot: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize the DuckDuckGo search engine.

        Args:
            max_results: Maximum number of search results
            region: Region code for search results
            safe_search: Whether to enable safe search
            llm: Language model for relevance filtering
            language: Language for content processing
            include_full_content: Whether to include full webpage content in results
        """
        max_results = min(max_results, 50)
        # Initialize the BaseSearchEngine with LLM, max_filtered_results, and max_results
        super().__init__(
            llm=llm,
            max_filtered_results=max_filtered_results,
            max_results=max_results,
            include_full_content=include_full_content,
            settings_snapshot=settings_snapshot,
        )
        self.region = region
        self.safe_search = safe_search
        self.language = language

        # Initialize the DuckDuckGo wrapper
        self.engine = DuckDuckGoSearchAPIWrapper(
            region=region,
            max_results=max_results,
            safesearch="moderate" if safe_search else "off",
        )

        # Initialize FullSearchResults if full content is requested
        self._init_full_search(
            web_search=self.engine,
            language=language,
            max_results=max_results,
            region=region,
            time_period="y",
            safe_search="Moderate" if safe_search else "Off",
        )

    def _get_previews(self, query: str) -> List[Dict[str, Any]]:
        """
        Get preview information (titles and snippets) for initial search results.

        Args:
            query: The search query

        Returns:
            List of preview dictionaries with 'id', 'title', and 'snippet' keys
        """
        try:
            # Get search results from DuckDuckGo
            results = self.engine.results(query, max_results=self.max_results)

            if not isinstance(results, list):
                return []

            # Process results to get previews
            previews = []
            for i, result in enumerate(results):
                preview = {
                    "id": result.get("link"),  # Use URL as ID for DDG
                    "title": result.get("title", ""),
                    "snippet": result.get("snippet", ""),
                    "link": result.get("link", ""),
                }

                previews.append(preview)

            return previews

        except Exception as e:
            error_msg = str(e)
            sanitized = self._sanitize_error_message(error_msg)
            logger.exception("Error getting DuckDuckGo previews: {}", sanitized)

            # Check for known rate limit patterns
            if "202 Ratelimit" in error_msg or "ratelimit" in error_msg.lower():
                raise RateLimitError(f"DuckDuckGo rate limit hit: {sanitized}")
            if "403" in error_msg or "forbidden" in error_msg.lower():
                raise RateLimitError(
                    f"DuckDuckGo access forbidden (possible rate limit): {sanitized}"
                )
            if (
                "timeout" in error_msg.lower()
                or "timed out" in error_msg.lower()
            ):
                # Timeouts can sometimes indicate rate limiting
                raise RateLimitError(
                    f"DuckDuckGo timeout (possible rate limit): {sanitized}"
                )

            return []

    def _get_full_content(
        self, relevant_items: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """
        Get full content for the relevant items by using FullSearchResults.

        Args:
            relevant_items: List of relevant preview dictionaries

        Returns:
            List of result dictionaries with full content
        """
        # If we have FullSearchResults, use it to get full content
        if hasattr(self, "full_search"):
            return self.full_search._get_full_content(relevant_items)

        # Otherwise, just return the relevant items without full content
        return relevant_items
