use std::io::Error as IoError;

use rapira::RapiraError;
use thiserror::Error;

use crate::types::ArmourError;

#[derive(Error, Debug)]
pub enum DbError {
    /// 400
    #[error("client error: {0}")]
    Client(&'static str),
    /// 404
    #[error("not found")]
    NotFound,
    /// 501
    #[error("not implemented")]
    NotImplemented,
    #[error("transactional error")]
    Transaction,
    #[error("storage logic error: {0}")]
    Internal(&'static str),
    #[error(transparent)]
    Io(#[from] IoError),
    #[error(transparent)]
    Rapira(#[from] RapiraError),
    #[error(transparent)]
    Armour(#[from] ArmourError),
    #[cfg(feature = "fjall")]
    #[cfg_attr(feature = "fjall", error(transparent))]
    Fjall(#[from] fjall::Error),
    // #[cfg(feature = "bytemuck")]
    // #[cfg_attr(feature = "bytemuck", error("bytemuck error: {0}"))]
    // Bytemuck(bytemuck::PodCastError),
}

impl DbError {
    pub fn status_code(&self) -> u16 {
        match self {
            DbError::Client(_) => 400,
            DbError::NotFound => 404,
            DbError::NotImplemented => 501,
            _ => 500,
        }
    }
    pub fn to_resp(&self) -> (u16, &'static str) {
        match self {
            DbError::Client(msg) => (400, msg),
            DbError::NotFound => (404, ""),
            DbError::NotImplemented => (501, ""),
            _ => {
                tracing::error!("{self}");
                (500, "")
            }
        }
    }
}

pub type DbResult<T> = Result<T, DbError>;
