"""
Base class for rating systems.
Following LDR's pattern from BaseSearchStrategy.

NOTE: The news rating system is intentionally separate from research session ratings.
News ratings serve a different purpose - they are used for:
1. Recommending new topics to users based on their preferences
2. Improving the news recommendation algorithm
3. Understanding which types of news content are most valuable to users

This is distinct from research session ratings which evaluate the quality of
research output. As per PR review discussion with djpetti.
"""

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

from ..core.utils import utc_now


class RelevanceRating(Enum):
    """Enum for relevance rating values."""

    UP = "up"
    DOWN = "down"


class QualityRating(Enum):
    """Enum for quality rating values."""

    ONE_STAR = 1
    TWO_STARS = 2
    THREE_STARS = 3
    FOUR_STARS = 4
    FIVE_STARS = 5


class BaseRatingSystem(ABC):
    """Abstract base class for all rating systems."""

    def __init__(self, storage_backend: Optional[Any] = None):
        """
        Initialize the base rating system.

        Args:
            storage_backend: Optional storage backend for ratings
        """
        self.storage_backend = storage_backend
        self.rating_type = self.__class__.__name__

    @abstractmethod
    def rate(
        self,
        user_id: str,
        card_id: str,
        rating_value: Any,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Record a rating from a user.

        Args:
            user_id: ID of the user making the rating
            card_id: ID of the card being rated
            rating_value: The rating value (type depends on implementation)
            metadata: Optional additional metadata

        Returns:
            Dict containing rating confirmation and any updates
        """
        pass

    @abstractmethod
    def get_rating(
        self, user_id: str, card_id: str
    ) -> Optional[Dict[str, Any]]:
        """
        Get a user's rating for a specific card.

        Args:
            user_id: ID of the user
            card_id: ID of the card

        Returns:
            Rating information or None if not rated
        """
        pass

    @abstractmethod
    def get_rating_type(self) -> str:
        """
        Get the type of rating this system handles.

        Returns:
            str: Rating type identifier
        """
        pass

    def get_recent_ratings(
        self, user_id: str, limit: int = 50
    ) -> List[Dict[str, Any]]:
        """
        Get recent ratings by a user.

        Args:
            user_id: ID of the user
            limit: Maximum number of ratings to return

        Returns:
            List of recent ratings
        """
        # Default implementation - should be overridden if storage is available
        logger.warning(
            f"get_recent_ratings not implemented for {self.rating_type}"
        )
        return []

    def get_card_ratings(
        self, card_id: str, rating_type: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Get aggregated ratings for a card.

        Args:
            card_id: ID of the card
            rating_type: Optional specific rating type to filter

        Returns:
            Aggregated rating information
        """
        # Default implementation - should be overridden
        logger.warning(
            f"get_card_ratings not implemented for {self.rating_type}"
        )
        return {"total": 0, "average": None}

    def remove_rating(self, user_id: str, card_id: str) -> bool:
        """
        Remove a user's rating for a card.

        Args:
            user_id: ID of the user
            card_id: ID of the card

        Returns:
            bool: True if rating was removed
        """
        # Default implementation
        logger.warning(f"remove_rating not implemented for {self.rating_type}")
        return False

    def _create_rating_record(
        self,
        user_id: str,
        card_id: str,
        rating_value: Any,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Create a standard rating record.

        Helper method for subclasses to create consistent rating records.
        """
        return {
            "user_id": user_id,
            "card_id": card_id,
            "rating_type": self.get_rating_type(),
            "rating_value": rating_value,
            "rated_at": utc_now().isoformat(),
            "metadata": metadata or {},
        }

    def _validate_rating_value(self, rating_value: Any) -> None:
        """
        Validate that a rating value is acceptable.
        Should be overridden by subclasses with specific validation.

        Args:
            rating_value: The value to validate

        Raises:
            ValueError: If the rating value is invalid
        """
        if rating_value is None:
            raise ValueError("Rating value cannot be None")


class QualityRatingSystem(BaseRatingSystem):
    """Rating system for article quality (1-5 stars)."""

    def get_rating_type(self) -> str:
        return "quality"

    def rate(
        self,
        user_id: str,
        card_id: str,
        rating_value: QualityRating,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Rate the quality of an article.

        Args:
            rating_value: QualityRating enum value (ONE_STAR through FIVE_STARS)
        """
        self._validate_rating_value(rating_value)

        record = self._create_rating_record(
            user_id, card_id, rating_value, metadata
        )

        # Store the rating (implementation depends on storage backend)
        if self.storage_backend:
            # Storage implementation would go here
            pass

        logger.info(
            f"User {user_id} rated card {card_id} quality: {rating_value.value} stars"
        )

        return {
            "success": True,
            "rating": record,
            "message": f"Quality rating of {rating_value.value} stars recorded",
        }

    def _validate_rating_value(self, rating_value: Any) -> None:
        """Validate that rating is a valid QualityRating enum value."""
        super()._validate_rating_value(rating_value)

        if not isinstance(rating_value, QualityRating):
            raise ValueError(
                "Quality rating must be a QualityRating enum value (ONE_STAR through FIVE_STARS)"
            )

    def get_rating(
        self, user_id: str, card_id: str
    ) -> Optional[Dict[str, Any]]:
        """Get a user's quality rating for a card."""
        # Implementation depends on storage backend
        if self.storage_backend:
            # Storage query would go here
            pass
        return None


class RelevanceRatingSystem(BaseRatingSystem):
    """Rating system for personal relevance (thumbs up/down)."""

    def get_rating_type(self) -> str:
        return "relevance"

    def rate(
        self,
        user_id: str,
        card_id: str,
        rating_value: RelevanceRating,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Rate the relevance of an article.

        Args:
            rating_value: RelevanceRating.UP or RelevanceRating.DOWN
        """
        self._validate_rating_value(rating_value)

        record = self._create_rating_record(
            user_id, card_id, rating_value, metadata
        )

        # Store the rating (implementation depends on storage backend)
        if self.storage_backend:
            # Storage implementation would go here
            pass

        logger.info(
            f"User {user_id} rated card {card_id} relevance: thumbs {rating_value.value}"
        )

        return {
            "success": True,
            "rating": record,
            "message": f"Relevance rating of thumbs {rating_value.value} recorded",
        }

    def _validate_rating_value(self, rating_value: Any) -> None:
        """Validate that rating is a valid RelevanceRating enum value."""
        super()._validate_rating_value(rating_value)

        if not isinstance(rating_value, RelevanceRating):
            raise ValueError(
                "Relevance rating must be RelevanceRating.UP or RelevanceRating.DOWN"
            )

    def get_rating(
        self, user_id: str, card_id: str
    ) -> Optional[Dict[str, Any]]:
        """Get a user's relevance rating for a card."""
        # Implementation depends on storage backend
        if self.storage_backend:
            # Storage query would go here
            pass
        return None
