"""
Centralized storage manager for all news system data.
Provides unified access to cards, subscriptions, ratings, and preferences.
"""

from enum import Enum
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta, timezone
from loguru import logger
from flask import g, has_app_context

from .card_factory import CardFactory
from .base_card import BaseCard
from .card_storage import SQLCardStorage
from .relevance_service import get_relevance_service
from ..subscription_manager.storage import SQLSubscriptionStorage
from ..rating_system.storage import SQLRatingStorage
from ..preference_manager.storage import SQLPreferenceStorage


class InteractionType(Enum):
    """Enum for card interaction types."""

    VIEW = "view"
    VOTE_UP = "vote_up"
    VOTE_DOWN = "vote_down"
    RESEARCH = "research"
    SHARE = "share"


class StorageManager:
    """
    Unified storage interface for the news system.
    Manages all data access across modules.
    """

    def __init__(self):
        """Initialize storage interfaces."""
        # Storage interfaces will be created on demand
        self._cards = None
        self._subscriptions = None
        self._ratings = None
        self._preferences = None

        # Card factory for reconstruction
        self.card_factory = CardFactory

        # Relevance service for business logic
        self.relevance_service = get_relevance_service()

        logger.info("StorageManager initialized")

    def _get_current_session(self):
        """Get the current database session from Flask context."""
        if has_app_context() and hasattr(g, "db_session") and g.db_session:
            return g.db_session
        else:
            # If no session in context, we need to create one
            # This will trigger register_activity
            return None

    @property
    def cards(self):
        """Get cards storage interface."""
        session = self._get_current_session()
        if session:
            return SQLCardStorage(session)
        else:
            # For now, create storage without session
            # This will fail when trying to use it
            if self._cards is None:
                raise RuntimeError(
                    "No database session available for news storage"
                )
            return self._cards

    @property
    def subscriptions(self):
        """Get subscriptions storage interface."""
        session = self._get_current_session()
        if session:
            return SQLSubscriptionStorage(session)
        else:
            if self._subscriptions is None:
                raise RuntimeError(
                    "No database session available for news storage"
                )
            return self._subscriptions

    @property
    def ratings(self):
        """Get ratings storage interface."""
        session = self._get_current_session()
        if session:
            return SQLRatingStorage(session)
        else:
            if self._ratings is None:
                raise RuntimeError(
                    "No database session available for news storage"
                )
            return self._ratings

    @property
    def preferences(self):
        """Get preferences storage interface."""
        session = self._get_current_session()
        if session:
            return SQLPreferenceStorage(session)
        else:
            if self._preferences is None:
                raise RuntimeError(
                    "No database session available for news storage"
                )
            return self._preferences

    def get_user_feed(
        self,
        user_id: str,
        limit: int = 20,
        offset: int = 0,
        include_seen: bool = True,
        card_types: Optional[List[str]] = None,
    ) -> List[BaseCard]:
        """
        Get personalized news feed for a user.

        Args:
            user_id: The user ID
            limit: Maximum cards to return
            offset: Pagination offset
            include_seen: Whether to include already viewed cards
            card_types: Filter by card types

        Returns:
            List of cards sorted by relevance
        """
        try:
            # Get user preferences
            user_prefs = self.preferences.get(user_id)

            # Get recent cards
            if user_prefs and user_prefs.get("liked_categories"):
                # Filter by user's preferred categories
                filters = {
                    "user_id": user_id,
                    "categories": user_prefs["liked_categories"],
                }
            else:
                filters = {"user_id": user_id}

            if card_types:
                filters["card_type"] = card_types

            # Get cards from storage
            cards_data = self.cards.list(
                filters=filters,
                limit=limit * 2,  # Get extra to filter
                offset=offset,
            )

            # Reconstruct card objects
            cards = []
            for data in cards_data:
                card = CardFactory.load_card(data["id"])
                if card:
                    cards.append(card)

            # Apply personalization
            cards = self.relevance_service.personalize_feed(
                cards, user_prefs, include_seen=include_seen
            )

            return cards[:limit]

        except Exception:
            logger.exception("Error getting user feed")
            return []

    def get_trending_news(
        self, hours: int = 24, limit: int = 10, min_impact: int = 7
    ) -> List[BaseCard]:
        """
        Get trending news across all users.

        Args:
            hours: Look back period
            limit: Maximum cards
            min_impact: Minimum impact score

        Returns:
            List of trending news cards
        """
        try:
            # Get recent high-impact cards
            cards = CardFactory.get_recent_cards(
                hours=hours, card_types=["news"], limit=limit * 2
            )

            # Use relevance service to filter and sort trending
            return self.relevance_service.filter_trending(
                cards, min_impact=min_impact, limit=limit
            )

        except Exception:
            logger.exception("Error getting trending news")
            return []

    def record_interaction(
        self,
        user_id: str,
        card_id: str,
        interaction_type: InteractionType,
        metadata: Optional[Dict] = None,
    ) -> bool:
        """
        Record user interaction with a card.

        Args:
            user_id: The user
            card_id: The card
            interaction_type: Type of interaction (view, vote, share, etc.)
            metadata: Additional data

        Returns:
            Success status
        """
        try:
            # Load the card
            card = CardFactory.load_card(card_id)
            if not card:
                return False

            # Update interaction data
            if interaction_type == InteractionType.VIEW:
                card.interaction["viewed"] = True
                card.interaction["last_viewed"] = datetime.now(timezone.utc)
                card.interaction["views"] = card.interaction.get("views", 0) + 1

            elif interaction_type == InteractionType.VOTE_UP:
                card.interaction["voted"] = "up"
                card.interaction["votes_up"] = (
                    card.interaction.get("votes_up", 0) + 1
                )
                # Record in ratings
                self.ratings.save(
                    {
                        "user_id": user_id,
                        "card_id": card_id,
                        "rating_type": "relevance",
                        "value": 1,
                    }
                )

            elif interaction_type == InteractionType.VOTE_DOWN:
                card.interaction["voted"] = "down"
                card.interaction["votes_down"] = (
                    card.interaction.get("votes_down", 0) + 1
                )
                # Record in ratings
                self.ratings.save(
                    {
                        "user_id": user_id,
                        "card_id": card_id,
                        "rating_type": "relevance",
                        "value": -1,
                    }
                )

            elif interaction_type == InteractionType.RESEARCH:
                card.interaction["researched"] = True
                card.interaction["research_count"] = (
                    card.interaction.get("research_count", 0) + 1
                )

            # Add metadata if provided
            if metadata:
                card.interaction[f"{interaction_type}_metadata"] = metadata

            # Save updated card
            return CardFactory.update_card(card)

        except Exception:
            logger.exception("Error recording interaction")
            return False

    def get_user_subscriptions(self, user_id: str) -> List[Any]:
        """
        Get all subscriptions for a user.

        Args:
            user_id: The user

        Returns:
            List of subscription objects
        """
        try:
            return self.subscriptions.list({"user_id": user_id})
        except Exception:
            logger.exception("Error getting user subscriptions")
            return []

    def get_user_stats(self, user_id: str) -> Dict[str, Any]:
        """
        Get statistics for a user.

        Args:
            user_id: The user

        Returns:
            Dictionary of statistics
        """
        try:
            # Get counts from various storages
            subscription_count = len(
                self.subscriptions.list({"user_id": user_id})
            )

            # Get rating counts
            ratings = self.ratings.list({"user_id": user_id})
            votes_up = sum(1 for r in ratings if r.get("value", 0) > 0)
            votes_down = sum(1 for r in ratings if r.get("value", 0) < 0)

            # Get card interaction stats
            user_cards = self.cards.list({"user_id": user_id})
            total_views = sum(
                c.get("interaction", {}).get("views", 0) for c in user_cards
            )

            return {
                "subscriptions": subscription_count,
                "votes_up": votes_up,
                "votes_down": votes_down,
                "total_views": total_views,
                "cards_created": len(user_cards),
                "member_since": user_cards[0]["created_at"]
                if user_cards
                else None,
            }

        except Exception:
            logger.exception("Error getting user stats")
            return {}

    def get_card(self, card_id: str) -> Optional[BaseCard]:
        """
        Get a single card by ID.

        Args:
            card_id: The card ID

        Returns:
            Card object or None
        """
        try:
            return CardFactory.load_card(card_id)
        except Exception:
            logger.exception("Error getting card")
            return None

    def get_card_interactions(self, card_id: str) -> List[Dict[str, Any]]:
        """
        Get all interactions for a card.

        Args:
            card_id: The card ID

        Returns:
            List of interaction records
        """
        try:
            # Get ratings which represent votes
            ratings = self.ratings.list({"card_id": card_id})

            # Convert to interaction format
            interactions = []
            for rating in ratings:
                interaction = {
                    "user_id": rating.get("user_id"),
                    "interaction_type": "vote",
                    "interaction_data": {
                        "vote": "up" if rating.get("value", 0) > 0 else "down"
                    },
                    "timestamp": rating.get("created_at"),
                }
                interactions.append(interaction)

            return interactions

        except Exception:
            logger.exception("Error getting card interactions")
            return []

    def update_card(self, card: BaseCard) -> bool:
        """
        Update a card in storage.

        Args:
            card: The card to update

        Returns:
            Success status
        """
        try:
            return CardFactory.update_card(card)
        except Exception:
            logger.exception("Error updating card")
            return False

    def cleanup_old_data(self, days: int = 30) -> Dict[str, int]:
        """
        Clean up old data from all storages.

        Args:
            days: Age threshold in days

        Returns:
            Counts of deleted items
        """
        try:
            cutoff = datetime.now(timezone.utc) - timedelta(days=days)
            counts = {}

            # Clean old cards (except subscribed ones)
            old_cards = self.cards.list(
                {"created_before": cutoff, "not_subscribed": True}
            )
            deleted_cards = 0
            for card_data in old_cards:
                if self.cards.delete(card_data["id"]):
                    deleted_cards += 1
            counts["cards"] = deleted_cards

            # Clean old ratings
            old_ratings = self.ratings.list({"created_before": cutoff})
            counts["ratings"] = len(old_ratings)
            for rating in old_ratings:
                self.ratings.delete(rating["id"])

            logger.info(f"Cleanup complete: {counts}")
            return counts

        except Exception:
            logger.exception("Error during cleanup")
            return {}


# Singleton instance
_storage_manager = None


def get_storage_manager() -> StorageManager:
    """Get or create the global StorageManager instance."""
    global _storage_manager
    if _storage_manager is None:
        _storage_manager = StorageManager()
    return _storage_manager
