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
from .full_search import FullSearchResults  # Import the FullSearchResults class


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

    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=5,
    ):
        """
        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
        """
        # 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,
        )
        self.region = region
        self.safe_search = safe_search
        self.language = language
        self.include_full_content = include_full_content

        # 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
        if include_full_content and llm:
            self.full_search = FullSearchResults(
                llm=llm,
                web_search=self.engine,
                language=language,
                max_results=max_results,
                region=region,
                time="y",
                safesearch="Moderate" if safe_search else "Off",
            )

    def run(
        self, query: str, research_context: Dict[str, Any] | None = None
    ) -> List[Dict[str, Any]]:
        """
        Execute a search using DuckDuckGo with the two-phase approach.
        Respects config parameters:
        - SEARCH_SNIPPETS_ONLY: If True, only returns snippets without full content
        - SKIP_RELEVANCE_FILTER: If True, returns all results without filtering

        Args:
            query: The search query
            research_context: Context from previous research to use.

        Returns:
            List of search results
        """
        logger.info("---Execute a search using DuckDuckGo---")

        # Implementation of the two-phase approach (from parent class)
        return super().run(query, research_context=research_context)

    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)
            logger.exception(f"Error getting DuckDuckGo previews: {error_msg}")

            # 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: {error_msg}")
            elif "403" in error_msg or "forbidden" in error_msg.lower():
                raise RateLimitError(
                    f"DuckDuckGo access forbidden (possible rate limit): {error_msg}"
                )
            elif (
                "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): {error_msg}"
                )

            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
