use common::build::BuildError;
use rocket::response::Responder;
use serde::Serialize;

#[derive(Debug, Serialize, Responder)]
pub enum ApiError {
    #[response(status = 404)]
    NotFound(String),

    #[response(status = 500)]
    InternalServerError(()),

    #[response(status = 500)]
    ServerError(String),

    #[response(status = 401)]
    Unauthorized(String),
    #[response(status = 403)]
    Forbidden(String),

    #[response(status = 400)]
    BadRequest(String),
}

impl From<std::io::Error> for ApiError {
    fn from(err: std::io::Error) -> Self {
        ApiError::InternalServerError(())
    }
}

impl From<BuildError> for ApiError {
    fn from(err: BuildError) -> Self {
        match err {
            BuildError::IoError(err) => ApiError::ServerError(err.to_string()),
            BuildError::Generic(err) => ApiError::ServerError(err),
        }
    }
}

impl From<DsxError> for ApiError {
    fn from(err: DsxError) -> Self {
        ApiError::ServerError(format!("{:?}: {}", err.r#type, err.message))
    }
}

#[derive(Debug)]
pub struct DsxError {
    pub message: String,
    pub r#type: ErrorType,
}

impl DsxError {
    pub fn new(message: impl AsRef<str>) -> Self {
        DsxError {
            message: message.as_ref().to_string(),
            r#type: ErrorType::Generic,
        }
    }

    pub fn with_context(message: impl AsRef<str>, r#type: ErrorType) -> Self {
        DsxError {
            message: message.as_ref().to_string(),
            r#type,
        }
    }
}

impl From<std::io::Error> for DsxError {
    fn from(err: std::io::Error) -> Self {
        Self::with_context(err.to_string(), ErrorType::IoError)
    }
}

#[derive(Default, Debug)]
pub enum ErrorType {
    BuildFailed,
    IoError,

    #[default]
    Generic,
    TarError,
}
