# app/modules/orders/exceptions.py
"""
Orders module exceptions.

This module provides exception classes for order operations including:
- Order management (not found, validation, status)
- Order item exceptions (unresolved, resolution)
- Invoice operations (PDF generation, status transitions)
"""

from typing import Any

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

__all__ = [
    # Order exceptions
    "OrderNotFoundException",
    "OrderAlreadyExistsException",
    "OrderValidationException",
    "InvalidOrderStatusException",
    "OrderCannotBeCancelledException",
    # Order item exception exceptions
    "OrderItemExceptionNotFoundException",
    "OrderHasUnresolvedExceptionsException",
    "ExceptionAlreadyResolvedException",
    "InvalidProductForExceptionException",
    # Invoice exceptions
    "InvoiceNotFoundException",
    "InvoiceSettingsNotFoundException",
    "InvoiceSettingsAlreadyExistException",
    "InvoiceValidationException",
    "InvoicePDFGenerationException",
    "InvoicePDFNotFoundException",
    "InvalidInvoiceStatusTransitionException",
    "OrderNotFoundForInvoiceException",
]


# =============================================================================
# Order Exceptions
# =============================================================================


class OrderNotFoundException(ResourceNotFoundException):
    """Raised when an order is not found."""

    def __init__(self, order_identifier: str):
        super().__init__(
            resource_type="Order",
            identifier=order_identifier,
            message=f"Order '{order_identifier}' not found",
            error_code="ORDER_NOT_FOUND",
        )


class OrderAlreadyExistsException(ValidationException):  # noqa: MOD025
    """Raised when trying to create a duplicate order."""

    def __init__(self, order_number: str):
        super().__init__(
            message=f"Order with number '{order_number}' already exists",
            details={"order_number": order_number},
        )
        self.error_code = "ORDER_ALREADY_EXISTS"


class OrderValidationException(ValidationException):
    """Raised when order data validation fails."""

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


class InvalidOrderStatusException(BusinessLogicException):  # noqa: MOD025
    """Raised when trying to set an invalid order status."""

    def __init__(self, current_status: str, new_status: str):
        super().__init__(
            message=f"Cannot change order status from '{current_status}' to '{new_status}'",
            error_code="INVALID_ORDER_STATUS_CHANGE",
            details={"current_status": current_status, "new_status": new_status},
        )


class OrderCannotBeCancelledException(BusinessLogicException):  # noqa: MOD025
    """Raised when order cannot be cancelled."""

    def __init__(self, order_number: str, reason: str):
        super().__init__(
            message=f"Order '{order_number}' cannot be cancelled: {reason}",
            error_code="ORDER_CANNOT_BE_CANCELLED",
            details={"order_number": order_number, "reason": reason},
        )


# =============================================================================
# Order Item Exception Exceptions
# =============================================================================


class OrderItemExceptionNotFoundException(ResourceNotFoundException):
    """Raised when an order item exception is not found."""

    def __init__(self, exception_id: int | str):
        super().__init__(
            resource_type="OrderItemException",
            identifier=str(exception_id),
            error_code="ORDER_ITEM_EXCEPTION_NOT_FOUND",
        )


class OrderHasUnresolvedExceptionsException(BusinessLogicException):
    """Raised when trying to confirm an order with unresolved exceptions."""

    def __init__(self, order_id: int, unresolved_count: int):
        super().__init__(
            message=(
                f"Order has {unresolved_count} unresolved product exception(s). "
                f"Please resolve all exceptions before confirming the order."
            ),
            error_code="ORDER_HAS_UNRESOLVED_EXCEPTIONS",
            details={
                "order_id": order_id,
                "unresolved_count": unresolved_count,
            },
        )


class ExceptionAlreadyResolvedException(BusinessLogicException):
    """Raised when trying to resolve an already resolved exception."""

    def __init__(self, exception_id: int):
        super().__init__(
            message=f"Exception {exception_id} has already been resolved",
            error_code="EXCEPTION_ALREADY_RESOLVED",
            details={"exception_id": exception_id},
        )


class InvalidProductForExceptionException(BusinessLogicException):
    """Raised when the product provided for resolution is invalid."""

    def __init__(self, product_id: int, reason: str):
        super().__init__(
            message=f"Cannot use product {product_id} for resolution: {reason}",
            error_code="INVALID_PRODUCT_FOR_EXCEPTION",
            details={"product_id": product_id, "reason": reason},
        )


# =============================================================================
# Invoice Exceptions
# =============================================================================


class InvoiceNotFoundException(ResourceNotFoundException):
    """Raised when an invoice is not found."""

    def __init__(self, invoice_id: int | str):
        super().__init__(
            resource_type="Invoice",
            identifier=str(invoice_id),
            error_code="INVOICE_NOT_FOUND",
        )


class InvoiceSettingsNotFoundException(ResourceNotFoundException):
    """Raised when invoice settings are not found for a store."""

    def __init__(self, store_id: int):
        super().__init__(
            resource_type="InvoiceSettings",
            identifier=str(store_id),
            message="Invoice settings not found. Create settings first.",
            error_code="INVOICE_SETTINGS_NOT_FOUND",
        )


class InvoiceSettingsAlreadyExistException(OrionException):  # noqa: MOD025
    """Raised when trying to create invoice settings that already exist."""

    def __init__(self, store_id: int):
        super().__init__(
            message=f"Invoice settings already exist for store {store_id}",
            error_code="INVOICE_SETTINGS_ALREADY_EXIST",
            status_code=409,
            details={"store_id": store_id},
        )


class InvoiceValidationException(BusinessLogicException):
    """Raised when invoice data validation fails."""

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


class InvoicePDFGenerationException(OrionException):
    """Raised when PDF generation fails."""

    def __init__(self, invoice_id: int, reason: str):
        super().__init__(
            message=f"Failed to generate PDF for invoice {invoice_id}: {reason}",
            error_code="INVOICE_PDF_GENERATION_FAILED",
            status_code=500,
            details={"invoice_id": invoice_id, "reason": reason},
        )


class InvoicePDFNotFoundException(ResourceNotFoundException):
    """Raised when invoice PDF file is not found."""

    def __init__(self, invoice_id: int):
        super().__init__(
            resource_type="InvoicePDF",
            identifier=str(invoice_id),
            message="PDF file not found. Generate the PDF first.",
            error_code="INVOICE_PDF_NOT_FOUND",
        )


class InvalidInvoiceStatusTransitionException(BusinessLogicException):
    """Raised when an invalid invoice status transition is attempted."""

    def __init__(
        self,
        current_status: str,
        new_status: str,
        reason: str | None = None,
    ):
        message = f"Cannot change invoice status from '{current_status}' to '{new_status}'"
        if reason:
            message += f": {reason}"

        super().__init__(
            message=message,
            error_code="INVALID_INVOICE_STATUS_TRANSITION",
            details={
                "current_status": current_status,
                "new_status": new_status,
            },
        )


class OrderNotFoundForInvoiceException(ResourceNotFoundException):  # noqa: MOD025
    """Raised when an order for invoice creation is not found."""

    def __init__(self, order_id: int):
        super().__init__(
            resource_type="Order",
            identifier=str(order_id),
            error_code="ORDER_NOT_FOUND_FOR_INVOICE",
        )
