# app/modules/billing/exceptions.py
"""
Billing module exceptions.

This module provides exception classes for billing operations including:
- Subscription management
- Payment processing (Stripe)
- Feature management
- Tier management
"""

from app.exceptions.base import (
    BusinessLogicException,
    ResourceNotFoundException,
    ServiceUnavailableException,
    ValidationException,
)

__all__ = [
    # Base billing exception
    "BillingException",
    # Subscription exceptions
    "SubscriptionNotFoundException",
    "NoActiveSubscriptionException",
    "SubscriptionNotCancelledException",
    "SubscriptionAlreadyCancelledException",
    # Tier exceptions
    "TierNotFoundException",
    "TierLimitExceededException",
    # Payment exceptions
    "PaymentSystemNotConfiguredException",
    "StripeNotConfiguredException",
    "StripePriceNotConfiguredException",
    "PaymentFailedException",
    # Webhook exceptions
    "InvalidWebhookSignatureException",
    "WebhookMissingSignatureException",
    "WebhookVerificationException",
    # Feature exceptions
    "FeatureNotFoundException",
    "InvalidFeatureCodesError",
]


# =============================================================================
# Base Billing Exception
# =============================================================================


class BillingException(BusinessLogicException):
    """Base exception for billing module errors."""

    def __init__(self, message: str, error_code: str = "BILLING_ERROR", details: dict | None = None):
        super().__init__(message=message, error_code=error_code, details=details)


# =============================================================================
# Subscription Exceptions
# =============================================================================


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

    def __init__(self, store_id: int):
        super().__init__(
            resource_type="Subscription",
            identifier=str(store_id),
            error_code="SUBSCRIPTION_NOT_FOUND",
        )


class NoActiveSubscriptionException(BusinessLogicException):
    """Raised when no active subscription exists for an operation that requires one."""

    def __init__(self, message: str = "No active subscription found"):
        super().__init__(
            message=message,
            error_code="NO_ACTIVE_SUBSCRIPTION",
        )


class SubscriptionNotCancelledException(BusinessLogicException):
    """Raised when trying to reactivate a subscription that is not cancelled."""

    def __init__(self):
        super().__init__(
            message="Subscription is not cancelled",
            error_code="SUBSCRIPTION_NOT_CANCELLED",
        )


class SubscriptionAlreadyCancelledException(BusinessLogicException):  # noqa: MOD025
    """Raised when trying to cancel an already cancelled subscription."""

    def __init__(self):
        super().__init__(
            message="Subscription is already cancelled",
            error_code="SUBSCRIPTION_ALREADY_CANCELLED",
        )


# =============================================================================
# Tier Exceptions
# =============================================================================


class TierNotFoundException(ResourceNotFoundException):
    """Raised when a subscription tier is not found."""

    def __init__(self, tier_code: str):
        super().__init__(
            resource_type="SubscriptionTier",
            identifier=tier_code,
            message=f"Subscription tier '{tier_code}' not found",
            error_code="TIER_NOT_FOUND",
        )
        self.tier_code = tier_code


class TierLimitExceededException(BillingException):
    """Raised when a tier limit is exceeded."""

    def __init__(self, message: str, limit_type: str, current: int, limit: int):
        super().__init__(
            message=message,
            error_code="TIER_LIMIT_EXCEEDED",
            details={
                "limit_type": limit_type,
                "current": current,
                "limit": limit,
            },
        )
        self.limit_type = limit_type
        self.current = current
        self.limit = limit


# =============================================================================
# Payment Exceptions
# =============================================================================


class PaymentSystemNotConfiguredException(ServiceUnavailableException):
    """Raised when the payment system (Stripe) is not configured."""

    def __init__(self):
        super().__init__(message="Payment system not configured")


class StripeNotConfiguredException(BillingException):
    """Raised when Stripe is not configured."""

    def __init__(self):
        super().__init__(
            message="Stripe is not configured",
            error_code="STRIPE_NOT_CONFIGURED",
        )


class StripePriceNotConfiguredException(BusinessLogicException):
    """Raised when Stripe price is not configured for a tier."""

    def __init__(self, tier_code: str):
        super().__init__(
            message=f"Stripe price not configured for tier '{tier_code}'",
            error_code="STRIPE_PRICE_NOT_CONFIGURED",
            details={"tier_code": tier_code},
        )
        self.tier_code = tier_code


class PaymentFailedException(BillingException):  # noqa: MOD025
    """Raised when a payment fails."""

    def __init__(self, message: str, stripe_error: str | None = None):
        details = {}
        if stripe_error:
            details["stripe_error"] = stripe_error
        super().__init__(
            message=message,
            error_code="PAYMENT_FAILED",
            details=details if details else None,
        )
        self.stripe_error = stripe_error


# =============================================================================
# Webhook Exceptions
# =============================================================================


class InvalidWebhookSignatureException(BusinessLogicException):
    """Raised when Stripe webhook signature verification fails."""

    def __init__(self, message: str = "Invalid webhook signature"):
        super().__init__(
            message=message,
            error_code="INVALID_WEBHOOK_SIGNATURE",
        )


class WebhookMissingSignatureException(BusinessLogicException):
    """Raised when Stripe webhook is missing the signature header."""

    def __init__(self):
        super().__init__(
            message="Missing Stripe-Signature header",
            error_code="WEBHOOK_MISSING_SIGNATURE",
        )


class WebhookVerificationException(BillingException):
    """Raised when webhook signature verification fails."""

    def __init__(self, message: str = "Invalid webhook signature"):
        super().__init__(
            message=message,
            error_code="WEBHOOK_VERIFICATION_FAILED",
        )


# =============================================================================
# Feature Exceptions
# =============================================================================


class FeatureNotFoundException(ResourceNotFoundException):
    """Raised when a feature is not found."""

    def __init__(self, feature_code: str):
        super().__init__(
            resource_type="Feature",
            identifier=feature_code,
            message=f"Feature '{feature_code}' not found",
        )
        self.feature_code = feature_code


class InvalidFeatureCodesError(ValidationException):
    """Invalid feature codes provided."""

    def __init__(self, invalid_codes: set[str]):
        codes_str = ", ".join(sorted(invalid_codes))
        super().__init__(
            message=f"Invalid feature codes: {codes_str}",
            details={"invalid_codes": list(invalid_codes)},
        )
        self.invalid_codes = invalid_codes
