"""
Base class for all news recommendation strategies.
Following LDR's pattern from BaseSearchStrategy.
"""

from abc import ABC, abstractmethod
from typing import List, Dict, Any, Optional, Callable
from loguru import logger

from ..core.base_card import NewsCard
from ..preference_manager.base_preference import BasePreferenceManager
from ..rating_system.base_rater import BaseRatingSystem


class BaseRecommender(ABC):
    """Abstract base class for all recommendation strategies."""

    def __init__(
        self,
        preference_manager: Optional[BasePreferenceManager] = None,
        rating_system: Optional[BaseRatingSystem] = None,
        topic_registry: Optional[Any] = None,
        search_system: Optional[Any] = None,
    ):
        """
        Initialize the base recommender with common dependencies.

        Args:
            preference_manager: Manager for user preferences
            rating_system: System for tracking ratings
            topic_registry: Registry of discovered topics
            search_system: LDR search system for executing queries
        """
        self.preference_manager = preference_manager
        self.rating_system = rating_system
        self.topic_registry = topic_registry
        self.search_system = search_system

        # Progress tracking (following LDR pattern)
        self.progress_callback: Optional[
            Callable[[str, Optional[int], dict], None]
        ] = None

        # Strategy name for identification
        self.strategy_name = self.__class__.__name__

    def set_progress_callback(
        self, callback: Callable[[str, Optional[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 generate_recommendations(
        self, user_id: str, context: Optional[Dict[str, Any]] = None
    ) -> List[NewsCard]:
        """
        Generate news recommendations for a user.

        Args:
            user_id: The user to generate recommendations for
            context: Optional context (e.g., current page, recent activity)

        Returns:
            List of NewsCard objects representing recommendations
        """
        pass

    def _get_user_preferences(self, user_id: str) -> Dict[str, Any]:
        """Get user preferences if preference manager is available."""
        if self.preference_manager:
            return self.preference_manager.get_preferences(user_id)
        return {}

    def _get_user_ratings(
        self, user_id: str, limit: int = 50
    ) -> List[Dict[str, Any]]:
        """Get recent user ratings if rating system is available."""
        if self.rating_system:
            return self.rating_system.get_recent_ratings(user_id, limit)
        return []

    def _execute_search(
        self, query: str, strategy: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Execute a search using the LDR search system.

        Args:
            query: The search query
            strategy: Optional search strategy to use

        Returns:
            Search results dictionary
        """
        if not self.search_system:
            logger.warning("No search system available for recommendations")
            return {"error": "Search system not configured"}

        try:
            # Use news strategy by default if available
            if strategy is None:
                strategy = "news_aggregation"

            # Execute search
            results: Dict[str, Any] = self.search_system.analyze_topic(query)
            return results

        except Exception:
            logger.exception("Error executing search for recommendations")
            return {"error": "Recommendation search failed"}

    def _filter_by_preferences(
        self, cards: List[NewsCard], preferences: Dict[str, Any]
    ) -> List[NewsCard]:
        """
        Filter cards based on user preferences.

        Args:
            cards: List of news cards to filter
            preferences: User preferences dictionary

        Returns:
            Filtered list of cards
        """
        filtered = cards

        # Filter by categories if specified
        if (
            "liked_categories" in preferences
            and preferences["liked_categories"]
        ):
            # Boost liked categories rather than filtering out others
            for card in filtered:
                if card.category in preferences["liked_categories"]:
                    card.metadata["preference_boost"] = 1.2

        # Filter by impact threshold
        if "impact_threshold" in preferences:
            threshold = preferences["impact_threshold"]
            filtered = [
                card for card in filtered if card.impact_score >= threshold
            ]

        # Apply disliked topics
        if "disliked_topics" in preferences and preferences["disliked_topics"]:
            filtered = [
                card
                for card in filtered
                if not any(
                    topic in card.topic.lower()
                    for topic in preferences["disliked_topics"]
                )
            ]

        return filtered

    def _sort_by_relevance(
        self, cards: List[NewsCard], user_id: str
    ) -> List[NewsCard]:
        """
        Sort cards by relevance to the user.
        Default implementation uses impact score and preference boost.

        Args:
            cards: List of cards to sort
            user_id: User ID for personalization

        Returns:
            Sorted list of cards
        """

        def calculate_score(card: NewsCard) -> float:
            # Base score from impact
            score: float = card.impact_score / 10.0

            # Apply preference boost if exists
            boost: float = float(card.metadata.get("preference_boost", 1.0))
            score *= boost

            # Could add more factors here (recency, etc.)
            return score

        # Sort by calculated score (highest first)
        return sorted(cards, key=calculate_score, reverse=True)

    def get_strategy_info(self) -> Dict[str, Any]:
        """Get information about this recommendation strategy."""
        return {
            "name": self.strategy_name,
            "has_preference_manager": self.preference_manager is not None,
            "has_rating_system": self.rating_system is not None,
            "has_search_system": self.search_system is not None,
            "description": self.__doc__ or "No description available",
        }
