//! Error type for the Kranth SDK.

use thiserror::Error;

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

#[derive(Debug, Error)]
pub enum Error {
    /// 401 — token missing or invalid.
    #[error("unauthorized: {message}")]
    Unauthorized { code: String, message: String },

    /// 402 — out of credits. Top up or upgrade plan.
    #[error("payment required: {message}")]
    PaymentRequired { code: String, message: String },

    /// 422 — body or query failed server-side validation.
    #[error("validation: {message}")]
    Validation { code: String, message: String },

    /// 429 — over the org's RPM. `retry_after` is the suggested back-off in seconds.
    #[error("rate limited (retry_after={retry_after:?}): {message}")]
    RateLimited {
        code: String,
        message: String,
        retry_after: Option<f64>,
    },

    /// Any other non-2xx HTTP error.
    #[error("api {status}: {code}: {message}")]
    Api {
        status: u16,
        code: String,
        message: String,
    },

    /// Network / TLS / serialisation failure.
    #[error("transport: {0}")]
    Transport(String),
}

impl Error {
    pub fn is_payment_required(&self) -> bool {
        matches!(self, Error::PaymentRequired { .. })
    }
    pub fn is_rate_limited(&self) -> bool {
        matches!(self, Error::RateLimited { .. })
    }
    pub fn is_unauthorized(&self) -> bool {
        matches!(self, Error::Unauthorized { .. })
    }
}

impl From<reqwest::Error> for Error {
    fn from(e: reqwest::Error) -> Self {
        Error::Transport(e.to_string())
    }
}

impl From<serde_json::Error> for Error {
    fn from(e: serde_json::Error) -> Self {
        Error::Transport(format!("json: {e}"))
    }
}
