use thiserror::Error;

#[derive(Error, Debug)]
pub enum CannonError {
    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("Configuration error: {0}")]
    Config(#[from] config::ConfigError),

    #[error("Redis error: {0}")]
    Redis(#[from] redis::RedisError),

    #[error("Not found: {0}")]
    NotFound(String),

    #[error("Invalid request: {0}")]
    BadRequest(String),

    #[error("Validation error: {0}")]
    ValidationError(String),

    #[error("Unauthorized")]
    Unauthorized,

    #[error("Forbidden: {0}")]
    Forbidden(String),

    #[error("Payment required: trial expired")]
    PaymentRequired,

    #[error("Usage limit exceeded: {0}")]
    QuotaExceeded(String),

    #[error("Conflict: {0}")]
    Conflict(String),

    #[error("Payload too large")]
    PayloadTooLarge,

    #[error("Internal error: {0}")]
    Internal(String),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

impl CannonError {
    pub fn code(&self) -> &'static str {
        match self {
            Self::Database(_) => "DATABASE_ERROR",
            Self::Config(_) => "CONFIG_ERROR",
            Self::Redis(_) => "REDIS_ERROR",
            Self::NotFound(_) => "NOT_FOUND",
            Self::BadRequest(_) => "BAD_REQUEST",
            Self::ValidationError(_) => "VALIDATION_ERROR",
            Self::Unauthorized => "UNAUTHORIZED",
            Self::Forbidden(_) => "FORBIDDEN",
            Self::PaymentRequired => "PAYMENT_REQUIRED",
            Self::Conflict(_) => "CONFLICT",
            Self::QuotaExceeded(_) => "QUOTA_EXCEEDED",
            Self::PayloadTooLarge => "PAYLOAD_TOO_LARGE",
            Self::Internal(_) => "INTERNAL_ERROR",
            Self::Other(_) => "UNKNOWN_ERROR",
        }
    }
}

pub type Result<T> = std::result::Result<T, CannonError>;
