use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
    #[error("API error: {0}")]
    Api(#[from] ApiError),

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

    #[error("Permission denied: {0}")]
    PermissionDenied(String),

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

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

    #[error("Service unavailable: {0}")]
    Unavailable(String),

    #[error("HTTP request failed: {0}")]
    Request(#[from] reqwest::Error),

    #[error("Failed to parse response: {0}")]
    Parse(#[from] serde_json::Error),

    #[error("Invalid URL: {0}")]
    InvalidUrl(#[from] url::ParseError),

    #[error("Authentication failed: {0}")]
    Auth(String),

    #[error("Token requires onboarding")]
    NeedsOnboarding,

    #[error("Device flow authorization pending")]
    DeviceFlowPending,

    #[error("Device flow expired")]
    DeviceFlowExpired,

    #[error("Archive extraction failed: {0}")]
    Extract(String),

    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
}

#[derive(Error, Debug)]
#[error("{message}")]
pub struct ApiError {
    pub status_code: Option<u16>,
    pub message: String,
}

impl Error {
    pub fn api(message: impl Into<String>) -> Self {
        Self::Api(ApiError {
            status_code: None,
            message: message.into(),
        })
    }

    pub fn from_status(status: u16, message: String) -> Self {
        match status {
            404 => Self::NotFound(message),
            401 | 403 => Self::PermissionDenied(message),
            409 => Self::Conflict(message),
            400 => Self::BadRequest(message),
            502 | 503 | 504 => Self::Unavailable(message),
            _ => Self::Api(ApiError {
                status_code: Some(status),
                message,
            }),
        }
    }
}

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