use core::fmt;

use serde::Deserialize;

#[derive(Deserialize, Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[serde(rename_all = "kebab-case")]
pub enum PasswordErrorKind {
    /// password field did not match with corfirm password field
    Mismatch,
    /// password was empty
    Empty,
}

impl fmt::Display for PasswordErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Mismatch => write!(f, "mismatch"),
            Self::Empty => write!(f, "empty"),
        }
    }
}

#[derive(Deserialize, Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
#[serde(rename_all = "kebab-case")]
pub(crate) enum ErrorCode {
    // Frontend specific
    JsSendError,
    JsReadError,
    PasswordError(PasswordErrorKind),
    // Backend
    InvalidCredentials,
    CouldNotCreateUser,
    DbError,
}

impl fmt::Display for ErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::JsSendError => write!(f, "error sending json"),
            Self::JsReadError => write!(f, "error reading json"),
            Self::PasswordError(k) => write!(f, "password error {k}"),
            Self::InvalidCredentials => write!(f, "invalid credentials"),
            Self::CouldNotCreateUser => write!(f, "could not create user"),
            Self::DbError => write!(f, "database error"),
        }
    }
}

/// General API error
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub(crate) struct ApiError {
    pub(crate) code: ErrorCode,
    pub(crate) message: String,
}

impl fmt::Display for ApiError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}: {}", self.code, self.message)
    }
}