use std::fmt::Display;

use ftypes::error::ErrorActionRequired;
use gtk::glib;

#[derive(Debug)]
pub enum ConfigurationError {
    DbPathNoFolder(std::path::PathBuf),
    CannotCreateDbFolder(std::path::PathBuf, std::io::Error),
}

impl Display for ConfigurationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ConfigurationError::DbPathNoFolder(p) => {
                writeln!(f, "provided path is not a folder {}", p.display())
            }
            ConfigurationError::CannotCreateDbFolder(p, e) => {
                writeln!(
                    f,
                    "cannot create the database folder {}: {}",
                    p.display(),
                    e
                )
            }
        }
    }
}

impl std::error::Error for ConfigurationError {}

#[derive(Debug)]
pub enum ApplicationError {
    IOError(std::io::Error),
    Glib(glib::Error),
    Libsecret(oo7::Error),
    ConfigurationError(ConfigurationError),
    Backend(ftypes::Error),
}

impl From<ftypes::Error> for ApplicationError {
    fn from(value: ftypes::Error) -> Self {
        Self::Backend(value)
    }
}

impl From<std::io::Error> for ApplicationError {
    fn from(e: std::io::Error) -> Self {
        ApplicationError::IOError(e)
    }
}

impl From<oo7::Error> for ApplicationError {
    fn from(e: oo7::Error) -> Self {
        ApplicationError::Libsecret(e)
    }
}

impl From<glib::Error> for ApplicationError {
    fn from(e: glib::Error) -> Self {
        ApplicationError::Glib(e)
    }
}

use gettextrs::gettext;

impl std::fmt::Display for ApplicationError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ApplicationError::IOError(_) => writeln!(f, "{}", gettext("I/O Error.")),
            ApplicationError::Glib(_) => {
                writeln!(f, "{}", gettext("Something glib-related failed."))
            }
            ApplicationError::Libsecret(_) => {
                writeln!(f, "{}", gettext("The communication with libsecret failed."))
            }
            ApplicationError::ConfigurationError(_) => writeln!(
                f,
                "{}",
                gettext("The application seems to be misconfigured.")
            ),
            ApplicationError::Backend(_) => {
                writeln!(f, "{}", gettext("Backend error"))
            }
        }
    }
}

impl ApplicationError {
    pub fn more_information(&self) -> String {
        match self {
            ApplicationError::IOError(e) => format!("{e:#?}"),
            ApplicationError::Glib(e) => format!("{e:#?}"),
            ApplicationError::Libsecret(e) => format!("{e:#?}"),
            ApplicationError::ConfigurationError(e) => match e {
                ConfigurationError::DbPathNoFolder(p) => {
                    let s = gettext("The database path at {} is no folder.");
                    s.replace("{}", &p.to_string_lossy())
                }
                ConfigurationError::CannotCreateDbFolder(p, e) => {
                    let s = gettext(
                        "The database path at {path} is cannot be created due to error {error}.",
                    );
                    s.replace("{path}", &p.to_string_lossy())
                        .replace("{error}", &e.to_string())
                }
            },
            ApplicationError::Backend(e) => {
                format!("{e:#?}")
            }
        }
    }

    pub fn should_report(&self) -> bool {
        match self {
            ApplicationError::IOError(_) => false,
            ApplicationError::Glib(_) => true,
            ApplicationError::Libsecret(_) => false,
            ApplicationError::ConfigurationError(_) => true,
            ApplicationError::Backend(e) => e.action_required() == ErrorActionRequired::Report,
        }
    }
}
