# app/modules/cms/exceptions.py
"""
CMS module exceptions.

This module provides exception classes for CMS operations including:
- Content page management
- Media file handling
- Store theme customization
"""

from typing import Any

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

__all__ = [
    # Content page exceptions
    "ContentPageNotFoundException",
    "ContentPageAlreadyExistsException",
    "ContentPageSlugReservedException",
    "ContentPageNotPublishedException",
    "UnauthorizedContentPageAccessException",
    "StoreNotAssociatedException",
    "NoPlatformSubscriptionException",
    "ContentPageValidationException",
    # Media exceptions
    "MediaNotFoundException",
    "MediaUploadException",
    "MediaValidationException",
    "UnsupportedMediaTypeException",
    "MediaFileTooLargeException",
    "MediaOptimizationException",
    "MediaDeleteException",
    # Theme exceptions
    "StoreThemeNotFoundException",
    "InvalidThemeDataException",
    "ThemePresetNotFoundException",
    "ThemeValidationException",
    "ThemePresetAlreadyAppliedException",
    "InvalidColorFormatException",
    "InvalidFontFamilyException",
    "ThemeOperationException",
]


# =============================================================================
# Content Page Exceptions
# =============================================================================


class ContentPageNotFoundException(ResourceNotFoundException):
    """Raised when a content page is not found."""

    def __init__(self, identifier: str | int | None = None):
        if identifier:
            message = f"Content page not found: {identifier}"
        else:
            message = "Content page not found"
        super().__init__(
            message=message,
            resource_type="ContentPage",
            identifier=str(identifier) if identifier else "unknown",
            error_code="CONTENT_PAGE_NOT_FOUND",
        )


class ContentPageAlreadyExistsException(ConflictException):  # noqa: MOD025
    """Raised when a content page with the same slug already exists."""

    def __init__(self, slug: str, store_id: int | None = None):
        if store_id:
            message = f"Content page with slug '{slug}' already exists for this store"
        else:
            message = f"Platform content page with slug '{slug}' already exists"
        super().__init__(
            message=message,
            error_code="CONTENT_PAGE_ALREADY_EXISTS",
            details={"slug": slug, "store_id": store_id} if store_id else {"slug": slug},
        )


class ContentPageSlugReservedException(ValidationException):  # noqa: MOD025
    """Raised when trying to use a reserved slug."""

    def __init__(self, slug: str):
        super().__init__(
            message=f"Content page slug '{slug}' is reserved",
            field="slug",
            details={"slug": slug},
        )
        self.error_code = "CONTENT_PAGE_SLUG_RESERVED"


class ContentPageNotPublishedException(BusinessLogicException):  # noqa: MOD025
    """Raised when trying to access an unpublished content page."""

    def __init__(self, slug: str):
        super().__init__(
            message=f"Content page '{slug}' is not published",
            error_code="CONTENT_PAGE_NOT_PUBLISHED",
            details={"slug": slug},
        )


class UnauthorizedContentPageAccessException(AuthorizationException):
    """Raised when a user tries to access/modify a content page they don't own."""

    def __init__(self, action: str = "access"):
        super().__init__(
            message=f"Cannot {action} content pages from other stores",
            error_code="CONTENT_PAGE_ACCESS_DENIED",
            details={"required_permission": f"content_page:{action}"},
        )


class StoreNotAssociatedException(AuthorizationException):  # noqa: MOD025
    """Raised when a user is not associated with a store."""

    def __init__(self):
        super().__init__(
            message="User is not associated with a store",
            error_code="STORE_NOT_ASSOCIATED",
            details={"required_permission": "store:member"},
        )


class NoPlatformSubscriptionException(BusinessLogicException):
    """Raised when a store is not subscribed to any platform."""

    def __init__(self, store_id: int | None = None):
        details = {}
        if store_id:
            details["store_id"] = store_id
        super().__init__(
            message="Store is not subscribed to any platform",
            error_code="NO_PLATFORM_SUBSCRIPTION",
            details=details if details else None,
        )


class ContentPageValidationException(ValidationException):  # noqa: MOD025
    """Raised when content page data validation fails."""

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


# =============================================================================
# Media Exceptions
# =============================================================================


class MediaNotFoundException(ResourceNotFoundException):
    """Raised when a media file is not found."""

    def __init__(self, media_id: int | str | None = None):
        if media_id:
            message = f"Media file '{media_id}' not found"
        else:
            message = "Media file not found"
        super().__init__(
            resource_type="MediaFile",
            identifier=str(media_id) if media_id else "unknown",
            message=message,
            error_code="MEDIA_NOT_FOUND",
        )


class MediaUploadException(BusinessLogicException):  # noqa: MOD025
    """Raised when media upload fails."""

    def __init__(self, message: str = "Media upload failed", details: dict[str, Any] | None = None):
        super().__init__(
            message=message,
            error_code="MEDIA_UPLOAD_FAILED",
            details=details,
        )


class MediaValidationException(ValidationException):
    """Raised when media validation fails (file type, size, etc.)."""

    def __init__(
        self,
        message: str = "Media validation failed",
        field: str | None = None,
        details: dict[str, Any] | None = None,
    ):
        super().__init__(message=message, field=field, details=details)
        self.error_code = "MEDIA_VALIDATION_FAILED"


class UnsupportedMediaTypeException(ValidationException):
    """Raised when media file type is not supported."""

    def __init__(self, file_type: str, allowed_types: list[str] | None = None):
        details = {"file_type": file_type}
        if allowed_types:
            details["allowed_types"] = allowed_types

        super().__init__(
            message=f"Unsupported media type: {file_type}",
            field="file",
            details=details,
        )
        self.error_code = "UNSUPPORTED_MEDIA_TYPE"


class MediaFileTooLargeException(ValidationException):
    """Raised when media file exceeds size limit."""

    def __init__(self, file_size: int, max_size: int, media_type: str = "file"):
        super().__init__(
            message=f"File size ({file_size} bytes) exceeds maximum allowed ({max_size} bytes) for {media_type}",
            field="file",
            details={
                "file_size": file_size,
                "max_size": max_size,
                "media_type": media_type,
            },
        )
        self.error_code = "MEDIA_FILE_TOO_LARGE"


class MediaOptimizationException(BusinessLogicException):
    """Raised when media optimization fails."""

    def __init__(self, message: str = "Only images can be optimized"):
        super().__init__(
            message=message,
            error_code="MEDIA_OPTIMIZATION_FAILED",
        )


class MediaDeleteException(BusinessLogicException):  # noqa: MOD025
    """Raised when media deletion fails."""

    def __init__(self, message: str, details: dict[str, Any] | None = None):
        super().__init__(
            message=message,
            error_code="MEDIA_DELETE_FAILED",
            details=details,
        )


# =============================================================================
# Theme Exceptions
# =============================================================================


class StoreThemeNotFoundException(ResourceNotFoundException):
    """Raised when a store theme is not found."""

    def __init__(self, store_identifier: str):
        super().__init__(
            resource_type="StoreTheme",
            identifier=store_identifier,
            message=f"Theme for store '{store_identifier}' not found",
            error_code="STORE_THEME_NOT_FOUND",
        )


class InvalidThemeDataException(ValidationException):  # noqa: MOD025
    """Raised when theme data is invalid."""

    def __init__(
        self,
        message: str = "Invalid theme data",
        field: str | None = None,
        details: dict[str, Any] | None = None,
    ):
        super().__init__(
            message=message,
            field=field,
            details=details,
        )
        self.error_code = "INVALID_THEME_DATA"


class ThemePresetNotFoundException(ResourceNotFoundException):
    """Raised when a theme preset is not found."""

    def __init__(self, preset_name: str, available_presets: list | None = None):
        super().__init__(
            resource_type="ThemePreset",
            identifier=preset_name,
            message=f"Theme preset '{preset_name}' not found",
            error_code="THEME_PRESET_NOT_FOUND",
        )
        if available_presets:
            self.details["available_presets"] = available_presets


class ThemeValidationException(ValidationException):
    """Raised when theme validation fails."""

    def __init__(
        self,
        message: str = "Theme validation failed",
        field: str | None = None,
        validation_errors: dict[str, str] | None = None,
    ):
        details = {}
        if validation_errors:
            details["validation_errors"] = validation_errors

        super().__init__(
            message=message,
            field=field,
            details=details,
        )
        self.error_code = "THEME_VALIDATION_FAILED"


class ThemePresetAlreadyAppliedException(BusinessLogicException):  # noqa: MOD025
    """Raised when trying to apply the same preset that's already active."""

    def __init__(self, preset_name: str, store_code: str):
        super().__init__(
            message=f"Preset '{preset_name}' is already applied to store '{store_code}'",
            error_code="THEME_PRESET_ALREADY_APPLIED",
            details={"preset_name": preset_name, "store_code": store_code},
        )


class InvalidColorFormatException(ValidationException):
    """Raised when color format is invalid."""

    def __init__(self, color_value: str, field: str):
        super().__init__(
            message=f"Invalid color format: {color_value}",
            field=field,
            details={"color_value": color_value},
        )
        self.error_code = "INVALID_COLOR_FORMAT"


class InvalidFontFamilyException(ValidationException):
    """Raised when font family is invalid."""

    def __init__(self, font_value: str, field: str):
        super().__init__(
            message=f"Invalid font family: {font_value}",
            field=field,
            details={"font_value": font_value},
        )
        self.error_code = "INVALID_FONT_FAMILY"


class ThemeOperationException(BusinessLogicException):
    """Raised when theme operation fails."""

    def __init__(self, operation: str, store_code: str, reason: str):
        super().__init__(
            message=f"Theme operation '{operation}' failed for store '{store_code}': {reason}",
            error_code="THEME_OPERATION_FAILED",
            details={
                "operation": operation,
                "store_code": store_code,
                "reason": reason,
            },
        )
