"""
Topic-based recommender that generates recommendations from news topics.
This is the primary recommender for v1.
"""

from typing import List, Dict, Any, Optional
from loguru import logger

from .base_recommender import BaseRecommender
from ..core.base_card import NewsCard
from ..core.card_factory import CardFactory
from ...search_system import AdvancedSearchSystem
from ...security import sanitize_for_log
from ...security.egress.policy import PolicyDeniedError


class TopicBasedRecommender(BaseRecommender):
    """
    Recommends news based on topics extracted from recent news analysis.

    This recommender:
    1. Gets recent news topics from the topic registry
    2. Filters based on user preferences
    3. Generates search queries for interesting topics
    4. Creates NewsCards from the results
    """

    def __init__(self, **kwargs):
        """Initialize the topic-based recommender."""
        super().__init__(**kwargs)
        self.max_recommendations = 5  # Default limit

    def generate_recommendations(
        self, user_id: str, context: Optional[Dict[str, Any]] = None
    ) -> List[NewsCard]:
        """
        Generate recommendations based on trending topics.

        Args:
            user_id: User to generate recommendations for
            context: Optional context like current news being viewed

        Returns:
            List of NewsCard recommendations
        """
        logger.info(
            f"Generating topic-based recommendations for user {user_id}"
        )

        recommendations = []

        try:
            # Update progress
            self._update_progress("Getting trending topics", 10)

            # Get trending topics
            trending_topics = self._get_trending_topics(context)

            # Filter by user preferences
            self._update_progress("Applying user preferences", 30)
            preferences = self._get_user_preferences(user_id)
            filtered_topics = self._filter_topics_by_preferences(
                trending_topics, preferences
            )

            # Generate recommendations for top topics
            self._update_progress("Generating news searches", 50)

            for i, topic in enumerate(
                filtered_topics[: self.max_recommendations]
            ):
                progress = 50 + (
                    40 * i / len(filtered_topics[: self.max_recommendations])
                )
                self._update_progress(f"Searching for: {topic}", int(progress))

                # Create search query
                query = self._generate_topic_query(topic)

                # Register with priority manager
                try:
                    # Execute search
                    card = self._create_recommendation_card(
                        topic, query, user_id
                    )
                    if card:
                        recommendations.append(card)

                except Exception:
                    logger.exception(
                        f"Error creating recommendation for topic '{sanitize_for_log(topic)}'"
                    )
                    continue

            self._update_progress("Recommendations complete", 100)

            # Sort by relevance
            recommendations = self._sort_by_relevance(recommendations, user_id)

            logger.info(
                f"Generated {len(recommendations)} recommendations for user {user_id}"
            )

        except Exception as e:
            logger.exception("Error generating recommendations")
            self._update_progress(f"Error: {str(e)}", 100)

        return recommendations

    def _get_trending_topics(
        self, context: Optional[Dict[str, Any]]
    ) -> List[str]:
        """
        Get trending topics to recommend.

        Args:
            context: Optional context

        Returns:
            List of trending topic strings
        """
        topics = []

        # Get from topic registry if available
        if self.topic_registry:
            topics.extend(
                self.topic_registry.get_trending_topics(hours=24, limit=20)
            )

        # Add context-based topics if provided
        if context:
            if "current_news_topics" in context:
                topics.extend(context["current_news_topics"])
            if "current_category" in context:
                # Could fetch related topics based on category
                pass

        # Fallback topics if none found
        if not topics:
            logger.warning("No trending topics found, using defaults")
            topics = [
                "artificial intelligence developments",
                "cybersecurity threats",
                "climate change",
                "economic policy",
                "technology innovation",
            ]

        return topics

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

        Args:
            topics: List of topics to filter
            preferences: User preferences

        Returns:
            Filtered list of topics
        """
        filtered = []

        # Get preference lists
        disliked_topics = [
            t.lower() for t in preferences.get("disliked_topics", [])
        ]
        interests = preferences.get("interests", {})

        for topic in topics:
            topic_lower = topic.lower()

            # Skip disliked topics
            if any(disliked in topic_lower for disliked in disliked_topics):
                continue

            # Boost topics matching interests
            boost = 1.0
            for interest, weight in interests.items():
                if interest.lower() in topic_lower:
                    boost = weight
                    break

            # Add with boost information
            filtered.append((topic, boost))

        # Sort by boost (highest first)
        filtered.sort(key=lambda x: x[1], reverse=True)

        # Return just the topics
        return [topic for topic, _ in filtered]

    def _generate_topic_query(self, topic: str) -> str:
        """
        Generate a search query for a topic.

        Args:
            topic: The topic to search for

        Returns:
            Search query string
        """
        # Add news-specific context
        return f"{topic} latest news today breaking developments"

    def _create_recommendation_card(
        self, topic: str, query: str, user_id: str
    ) -> Optional[NewsCard]:
        """
        Create a news card from a topic recommendation.

        Args:
            topic: The topic
            query: The search query used
            user_id: The user ID

        Returns:
            NewsCard or None if search fails
        """
        llm = None
        search = None
        search_system = None
        try:
            # Use news search strategy
            from ...config.llm_config import get_llm
            from ...config.search_config import get_search

            try:
                llm = get_llm()
            except ValueError:
                # Configuration not set (e.g. llm.model empty). User issue,
                # not a runtime fault. Log a single concise warning per
                # scheduled topic — no stack trace, since this scheduler
                # runs repeatedly and we don't want to spam the log.
                # Topic is externally derived (news content); sanitize
                # before interpolation to prevent log injection.
                logger.warning(
                    f"Skipping news recommendation for topic "
                    f"'{sanitize_for_log(topic)}': "
                    "LLM not configured. Set llm.model in Settings."
                )
                return None
            except PolicyDeniedError as exc:
                # The egress policy refused the LLM (no snapshot in this
                # background path + non-local provider). Recurring
                # scheduler — log concisely, do not retry.
                logger.warning(
                    f"Skipping news recommendation for topic "
                    f"'{sanitize_for_log(topic)}': "
                    f"LLM blocked by egress policy ({exc.decision.reason}). "
                    "Use a local provider (ollama/lmstudio/llamacpp) or "
                    "thread settings_snapshot through this caller."
                )
                return None
            search = get_search(llm_instance=llm)
            search_system = AdvancedSearchSystem(
                llm=llm, search=search, strategy_name="news"
            )

            # Mark as news search to use priority system
            results = search_system.analyze_topic(query, is_news_search=True)

            if "error" in results:
                logger.error(
                    f"Search failed for topic '{sanitize_for_log(topic)}': "
                    f"{sanitize_for_log(str(results['error']))}"
                )
                return None

            # Check if we have news items directly from the search
            news_items = results.get("news_items", [])

            # Use the news items from search results
            news_data = {
                "items": news_items,
                "item_count": len(news_items),
                "big_picture": results.get("formatted_findings", ""),
                "topics": [],
            }

            if not news_items:
                logger.warning(
                    f"No news items found for topic '{sanitize_for_log(topic)}'"
                )
                return None

            # Create card using factory
            # Use the most impactful news item as the main content
            main_item = max(news_items, key=lambda x: x.get("impact_score", 0))

            card = CardFactory.create_news_card_from_analysis(
                news_item=main_item,
                source_search_id=str(results.get("search_id") or ""),
                user_id=user_id,
                additional_metadata={
                    "recommender": self.strategy_name,
                    "original_topic": topic,
                    "query_used": query,
                    "total_items_found": len(news_items),
                    "big_picture": news_data.get("big_picture", ""),
                    "topics_extracted": news_data.get("topics", []),
                },
            )

            # Add the full analysis as the first version
            if card:
                card.add_version(
                    research_results={
                        "search_results": results,
                        "news_analysis": news_data,
                        "query": query,
                        "strategy": "news_aggregation",
                    },
                    query=query,
                    strategy="news_aggregation",
                )

            return card

        except Exception:
            logger.exception(
                f"Error creating recommendation card for topic "
                f"'{sanitize_for_log(topic)}'"
            )
            return None
        finally:
            from ...utilities.resource_utils import safe_close

            safe_close(search_system, "news search system", allow_none=True)
            safe_close(search, "news search engine", allow_none=True)
            safe_close(llm, "news LLM", allow_none=True)


class SearchBasedRecommender(BaseRecommender):
    """
    Recommends news based on user's recent searches.
    Only works if search tracking is enabled.
    """

    def generate_recommendations(
        self, user_id: str, context: Optional[Dict[str, Any]] = None
    ) -> List[NewsCard]:
        """
        Generate recommendations from user's search history.

        Args:
            user_id: User to generate recommendations for
            context: Optional context

        Returns:
            List of NewsCard recommendations
        """
        logger.info(
            f"Generating search-based recommendations for user {user_id}"
        )

        # This would need access to search history
        # For now, return empty since search tracking is OFF by default
        logger.warning(
            "Search-based recommendations not available - search tracking is disabled"
        )
        return []

        # Future implementation would:
        # 1. Get user's recent searches
        # 2. Transform them to news queries
        # 3. Create recommendation cards
