// Code generated by TofuPilot SDK Generator. DO NOT EDIT.

use serde::{Deserialize, Serialize};

/// Result type alias for TofuPilot operations.
pub type Result<T> = std::result::Result<T, Error>;

/// Error types returned by the TofuPilot API.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// HTTP transport error (network, timeout, etc.)
    #[error("HTTP error: {0}")]
    Http(#[from] reqwest::Error),

    /// JSON serialization/deserialization error
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),

    /// API returned a bad request (400)
    #[error("Bad request: {0}")]
    BadRequest(ApiError),

    /// API returned unauthorized (401)
    #[error("Unauthorized: {0}")]
    Unauthorized(ApiError),

    /// API returned forbidden (403)
    #[error("Forbidden: {0}")]
    Forbidden(ApiError),

    /// API returned not found (404)
    #[error("Not found: {0}")]
    NotFound(ApiError),

    /// API returned conflict (409)
    #[error("Conflict: {0}")]
    Conflict(ApiError),

    /// API returned unprocessable content (422)
    #[error("Unprocessable content: {0}")]
    UnprocessableContent(ApiError),

    /// API returned internal server error (500)
    #[error("Internal server error: {0}")]
    InternalServerError(ApiError),

    /// API returned bad gateway (502)
    #[error("Bad gateway: {0}")]
    BadGateway(ApiError),

    /// API returned rate limited (429)
    #[error("Rate limited: {0}")]
    RateLimited(ApiError),

    /// API returned an unexpected status code
    #[error("Unexpected status {status}: {body}")]
    UnexpectedStatus {
        status: u16,
        body: String,
    },

    /// File I/O error (reading/writing local files)
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    /// Client-side validation error (missing required fields, invalid arguments)
    #[error("Validation error: {0}")]
    Validation(String),
}

/// Structured error response from the API.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApiError {
    /// Error message
    pub message: String,
    /// Error code
    #[serde(default)]
    pub code: String,
    /// Detailed issues
    #[serde(default)]
    pub issues: Vec<ApiErrorIssue>,
}

impl std::fmt::Display for ApiError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.message)?;
        if !self.issues.is_empty() {
            write!(f, " (")?;
            for (i, issue) in self.issues.iter().enumerate() {
                if i > 0 {
                    write!(f, ", ")?;
                }
                write!(f, "{}", issue.message)?;
            }
            write!(f, ")")?;
        }
        Ok(())
    }
}

/// Individual issue within an API error.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApiErrorIssue {
    /// Issue message
    pub message: String,
}

impl Error {
    /// Parse an API error response from a status code and body.
    pub(crate) fn from_response(status: u16, body: &str) -> Self {
        let api_error = serde_json::from_str::<ApiError>(body).unwrap_or_else(|_| ApiError {
            message: body.to_string(),
            code: status.to_string(),
            issues: vec![],
        });

        match status {
            400 => Error::BadRequest(api_error),
            401 => Error::Unauthorized(api_error),
            403 => Error::Forbidden(api_error),
            404 => Error::NotFound(api_error),
            409 => Error::Conflict(api_error),
            422 => Error::UnprocessableContent(api_error),
            429 => Error::RateLimited(api_error),
            500 => Error::InternalServerError(api_error),
            502 => Error::BadGateway(api_error),
            _ => Error::UnexpectedStatus {
                status,
                body: body.to_string(),
            },
        }
    }

    /// Returns `true` if this error is retryable (transport errors, 429, or 5xx).
    pub fn is_retryable(&self) -> bool {
        match self {
            Error::Http(_) => true,
            Error::RateLimited(_)
            | Error::InternalServerError(_)
            | Error::BadGateway(_) => true,
            Error::UnexpectedStatus { status, .. } => *status == 429 || *status >= 500,
            _ => false,
        }
    }
}
