"""
Custom exceptions for the news API module.

These exceptions are used to provide structured error handling
that can be caught by Flask error handlers and converted to
appropriate JSON responses.
"""

from typing import Optional, Dict, Any


class NewsAPIException(Exception):
    """Base exception for all news API related errors."""

    def __init__(
        self,
        message: str,
        status_code: int = 500,
        error_code: Optional[str] = None,
        details: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize the news API exception.

        Args:
            message: Human-readable error message
            status_code: HTTP status code for the error
            error_code: Machine-readable error code for API consumers
            details: Additional error details/context
        """
        super().__init__(message)
        self.message = message
        self.status_code = status_code
        self.error_code = error_code or self.__class__.__name__
        self.details = details or {}

    def to_dict(self) -> Dict[str, Any]:
        """Convert exception to dictionary for JSON response."""
        result = {
            "error": self.message,
            "error_code": self.error_code,
            "status_code": self.status_code,
        }
        if self.details:
            result["details"] = self.details
        return result


class NewsFeatureDisabledException(NewsAPIException):
    """Raised when the news feature is disabled in settings."""

    def __init__(self, message: str = "News system is disabled"):
        super().__init__(message, status_code=503, error_code="NEWS_DISABLED")


class InvalidLimitException(NewsAPIException):
    """Raised when an invalid limit parameter is provided."""

    def __init__(self, limit: int):
        super().__init__(
            f"Invalid limit: {limit}. Limit must be at least 1",
            status_code=400,
            error_code="INVALID_LIMIT",
            details={"provided_limit": limit, "min_limit": 1},
        )


class SubscriptionNotFoundException(NewsAPIException):
    """Raised when a requested subscription is not found."""

    def __init__(self, subscription_id: str):
        super().__init__(
            f"Subscription not found: {subscription_id}",
            status_code=404,
            error_code="SUBSCRIPTION_NOT_FOUND",
            details={"subscription_id": subscription_id},
        )


class SubscriptionCreationException(NewsAPIException):
    """Raised when subscription creation fails."""

    def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
        super().__init__(
            f"Failed to create subscription: {message}",
            status_code=500,
            error_code="SUBSCRIPTION_CREATE_FAILED",
            details=details,
        )


class SubscriptionUpdateException(NewsAPIException):
    """Raised when subscription update fails."""

    def __init__(self, subscription_id: str, message: str):
        super().__init__(
            f"Failed to update subscription {subscription_id}: {message}",
            status_code=500,
            error_code="SUBSCRIPTION_UPDATE_FAILED",
            details={"subscription_id": subscription_id},
        )


class SubscriptionDeletionException(NewsAPIException):
    """Raised when subscription deletion fails."""

    def __init__(self, subscription_id: str, message: str):
        super().__init__(
            f"Failed to delete subscription {subscription_id}: {message}",
            status_code=500,
            error_code="SUBSCRIPTION_DELETE_FAILED",
            details={"subscription_id": subscription_id},
        )


class DatabaseAccessException(NewsAPIException):
    """Raised when database access fails."""

    def __init__(self, operation: str, message: str):
        super().__init__(
            f"Database error during {operation}: {message}",
            status_code=500,
            error_code="DATABASE_ERROR",
            details={"operation": operation},
        )


class NewsFeedGenerationException(NewsAPIException):
    """Raised when news feed generation fails."""

    def __init__(self, message: str, user_id: Optional[str] = None):
        details = {}
        if user_id:
            details["user_id"] = user_id
        super().__init__(
            f"Failed to generate news feed: {message}",
            status_code=500,
            error_code="FEED_GENERATION_FAILED",
            details=details,
        )


class ResearchProcessingException(NewsAPIException):
    """Raised when processing research items for news fails."""

    def __init__(self, message: str, research_id: Optional[str] = None):
        details = {}
        if research_id:
            details["research_id"] = research_id
        super().__init__(
            f"Failed to process research item: {message}",
            status_code=500,
            error_code="RESEARCH_PROCESSING_FAILED",
            details=details,
        )


class NotImplementedException(NewsAPIException):
    """Raised when a feature is not yet implemented."""

    def __init__(self, feature: str):
        super().__init__(
            f"Feature not yet implemented: {feature}",
            status_code=501,
            error_code="NOT_IMPLEMENTED",
            details={"feature": feature},
        )


class InvalidParameterException(NewsAPIException):
    """Raised when invalid parameters are provided to API functions."""

    def __init__(self, parameter: str, value: Any, message: str):
        super().__init__(
            f"Invalid parameter '{parameter}': {message}",
            status_code=400,
            error_code="INVALID_PARAMETER",
            details={"parameter": parameter, "value": value},
        )


class SchedulerNotificationException(NewsAPIException):
    """Raised when scheduler notification fails (non-critical)."""

    def __init__(self, action: str, message: str):
        super().__init__(
            f"Failed to notify scheduler about {action}: {message}",
            status_code=500,
            error_code="SCHEDULER_NOTIFICATION_FAILED",
            details={"action": action},
        )
