use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use chrono::ParseError;
use leptos::ServerFnError;
use log::error;

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum AppError {
    HourParseError,
    ServerError(String),
    FatalError(String),
    SlotParseError,
    MailAddrParseErr(String),
    MailSendError(String),
    FileIOError(String)
}

impl AppError {
    fn as_string(&self) -> String {
        match self {
            AppError::HourParseError => {"Hour parse error".to_string()},
            AppError::ServerError(e) => {format!("Server error: {}", e)},
            AppError::FatalError(e) => {format!("Fatal error: {}", e)},
            AppError::SlotParseError => {"Book slot parse error".to_string()},
            AppError::MailAddrParseErr(e) => {format!("Cannot parse email address: {}", e)},
            AppError::MailSendError(e) => {format!("Cannot send email: {}", e)},
            AppError::FileIOError(e) => {format!("IO error: {}", e)}
        }
    }
}

fn log_error(error: &dyn ToString) {
    error!("Application error: {}", error.to_string());
}

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

impl Error for AppError {}

impl From<ServerFnError> for AppError {
    fn from(value: ServerFnError) -> Self {
        log_error(&value);
        AppError::ServerError(value.to_string())
    }
}

#[cfg(feature = "ssr")]
impl From<sqlx::Error> for AppError {
    fn from(value: sqlx::Error) -> Self {
        log_error(&value);
        AppError::FatalError(value.to_string())
    }
}

impl From<ParseError> for AppError {
    fn from(value: ParseError) -> Self {
        log_error(&value);
        AppError::HourParseError
    }
}

#[cfg(feature = "ssr")]
impl From<lettre::address::AddressError> for AppError {
    fn from(value: lettre::address::AddressError) -> Self {
        log_error(&value);
        AppError::MailAddrParseErr(value.to_string())
    }
}

#[cfg(feature = "ssr")]
impl From<lettre::error::Error> for AppError {
    fn from(value: lettre::error::Error) -> Self {
        log_error(&value);
        AppError::MailSendError(value.to_string())
    }
}

#[cfg(feature = "ssr")]
impl From<lettre::transport::smtp::Error> for AppError {
    fn from(value: lettre::transport::smtp::Error) -> Self {
        log_error(&value);
        AppError::MailSendError(value.to_string())
    }
}

#[cfg(feature = "ssr")]
impl From<lettre::transport::file::Error> for AppError {
    fn from(value: lettre::transport::file::Error) -> Self {
        log_error(&value);
        AppError::MailSendError(value.to_string())
    }
}

#[cfg(feature = "ssr")]
impl From<std::io::Error> for AppError {
    fn from(value: std::io::Error) -> Self {
        log_error(&value);
        AppError::FileIOError(value.to_string())
    }
}