"""
Base class for all search strategies.
Defines the common interface and shared functionality for different search approaches.
"""

from abc import ABC, abstractmethod
from typing import Callable, Dict, List, Optional

from loguru import logger


class BaseSearchStrategy(ABC):
    """Abstract base class for all search strategies."""

    def __init__(
        self,
        all_links_of_system=None,
        settings_snapshot=None,
        questions_by_iteration=None,
        search_original_query: bool = True,
    ):
        """Initialize the base strategy with common attributes.

        Args:
            all_links_of_system: List to store all discovered links
            settings_snapshot: Settings snapshot for configuration
            questions_by_iteration: Dictionary of questions by iteration
            search_original_query: Whether to include the original query in the first iteration
        """
        # Log strategy initialization
        strategy_name = self.__class__.__name__
        logger.info(f"Initializing strategy: {strategy_name}")
        logger.debug(
            f"Strategy {strategy_name} - search_original_query: {search_original_query}"
        )

        self.progress_callback = None
        # Create a new dict if None is provided (avoiding mutable default argument)
        self.questions_by_iteration = (
            questions_by_iteration if questions_by_iteration is not None else {}
        )
        # Create a new list if None is provided (avoiding mutable default argument)
        self.all_links_of_system = (
            all_links_of_system if all_links_of_system is not None else []
        )
        self.settings_snapshot = settings_snapshot or {}
        self.search_original_query = search_original_query

    def get_setting(self, key: str, default=None):
        """Get a setting value from the snapshot."""
        if key in self.settings_snapshot:
            value = self.settings_snapshot[key]
            # Extract value from dict structure if needed
            if isinstance(value, dict) and "value" in value:
                return value["value"]
            return value
        return default

    def set_progress_callback(
        self, callback: Callable[[str, int, dict], None]
    ) -> None:
        """Set a callback function to receive progress updates."""
        self.progress_callback = callback

    def _update_progress(
        self,
        message: str,
        progress_percent: Optional[int] = None,
        metadata: Optional[dict] = None,
    ) -> None:
        """Send a progress update via the callback if available."""
        if self.progress_callback:
            self.progress_callback(message, progress_percent, metadata or {})

    @abstractmethod
    def analyze_topic(self, query: str) -> Dict:
        """
        Analyze a topic using the strategy's specific approach.

        Args:
            query: The research query to analyze

        Returns:
            Dict containing:
            - findings: List of research findings
            - iterations: Number of iterations completed
            - questions: Questions generated by iteration
            - formatted_findings: Formatted output
            - current_knowledge: Accumulated knowledge
            - error: Optional error message
        """
        pass

    def _validate_search_engine(self) -> bool:
        """
        Validate that the search engine is available and configured.

        Returns:
            bool: True if search engine is available, False otherwise
        """
        if not hasattr(self, "search") or self.search is None:
            error_msg = "Error: No search engine available. Please check your configuration."
            self._update_progress(
                error_msg,
                100,
                {
                    "phase": "error",
                    "error": "No search engine available",
                    "status": "failed",
                },
            )
            return False
        return True

    def _handle_search_error(
        self, error: Exception, question: str, progress_base: int
    ) -> List:
        """
        Handle errors during search execution.

        Args:
            error: The exception that occurred
            question: The question being searched
            progress_base: The current progress percentage

        Returns:
            List: Empty list to continue processing
        """
        error_msg = f"Error during search: {error!s}"
        logger.error(f"SEARCH ERROR: {error_msg}")
        self._update_progress(
            error_msg,
            progress_base + 2,
            {"phase": "search_error", "error": str(error)},
        )
        return []

    def _handle_analysis_error(
        self, error: Exception, question: str, progress_base: int
    ) -> None:
        """
        Handle errors during result analysis.

        Args:
            error: The exception that occurred
            question: The question being analyzed
            progress_base: The current progress percentage
        """
        error_msg = f"Error analyzing results: {error!s}"
        logger.info(f"ANALYSIS ERROR: {error_msg}")
        self._update_progress(
            error_msg,
            progress_base + 10,
            {"phase": "analysis_error", "error": str(error)},
        )

    def _emit_question_generation_progress(
        self,
        iteration: int,
        progress_percent: int,
        source_count: int = 0,
        query: str = "",
    ) -> None:
        """
        Emit progress update for question generation phase.

        Args:
            iteration: Current iteration number
            progress_percent: Current progress percentage
            source_count: Number of sources found (for iteration > 1)
            query: The original research query (for iteration 1)
        """
        if iteration == 1:
            self._update_progress(
                f"Generating initial search questions:\n• {query}",
                progress_percent,
                {
                    "phase": "question_generation",
                    "type": "milestone",
                    "iteration": iteration,
                },
            )
        else:
            # Show previous questions and source count
            prev_questions = self.questions_by_iteration.get(iteration - 1, [])
            prev_display = "\n".join(f"• {q}" for q in prev_questions[:3])
            if len(prev_questions) > 3:
                prev_display += f"\n... and {len(prev_questions) - 3} more"
            self._update_progress(
                f"Generating questions from {source_count} sources:\n{prev_display}",
                progress_percent,
                {
                    "phase": "question_generation",
                    "type": "milestone",
                    "iteration": iteration,
                    "source_count": source_count,
                },
            )

    def _emit_searching_progress(
        self,
        iteration: int,
        questions: List[str],
        progress_percent: int,
    ) -> None:
        """
        Emit progress update for searching phase with questions as milestone.

        Args:
            iteration: Current iteration number
            questions: List of questions being searched
            progress_percent: Current progress percentage
        """
        questions_display = "\n".join(f"• {q}" for q in questions[:5])
        if len(questions) > 5:
            questions_display += f"\n... and {len(questions) - 5} more"
        self._update_progress(
            f"Searching iteration {iteration}:\n{questions_display}",
            progress_percent,
            {
                "phase": "parallel_search",
                "type": "milestone",
                "iteration": iteration,
                "questions": questions,
            },
        )
