#![allow(deprecated)]
use std::{num::NonZeroU16, str::FromStr};
use lettre::message::Mailbox;
use schemars::JsonSchema;
use serde::{de::Error, Deserialize, Serialize};
use super::ConfigurationSection;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct Credentials {
    pub username: String,
    pub password: String,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum EmailSmtpMode {
    Plain,
    StartTls,
    Tls,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "snake_case")]
pub enum EmailTransportKind {
    #[default]
    Blackhole,
    Smtp,
    Sendmail,
}
fn default_email() -> String {
    r#""Authentication Service" <root@localhost>"#.to_owned()
}
#[allow(clippy::unnecessary_wraps)]
fn default_sendmail_command() -> Option<String> {
    Some("sendmail".to_owned())
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct EmailConfig {
    #[serde(default = "default_email")]
    #[schemars(email)]
    pub from: String,
    #[serde(default = "default_email")]
    #[schemars(email)]
    pub reply_to: String,
    transport: EmailTransportKind,
    #[serde(skip_serializing_if = "Option::is_none")]
    mode: Option<EmailSmtpMode>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[schemars(with = "Option<crate::schema::Hostname>")]
    hostname: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[schemars(range(min = 1, max = 65535))]
    port: Option<NonZeroU16>,
    #[serde(skip_serializing_if = "Option::is_none")]
    username: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    password: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[schemars(default = "default_sendmail_command")]
    command: Option<String>,
}
impl EmailConfig {
    #[must_use]
    pub fn transport(&self) -> EmailTransportKind {
        self.transport
    }
    #[must_use]
    pub fn mode(&self) -> Option<EmailSmtpMode> {
        self.mode
    }
    #[must_use]
    pub fn hostname(&self) -> Option<&str> {
        self.hostname.as_deref()
    }
    #[must_use]
    pub fn port(&self) -> Option<NonZeroU16> {
        self.port
    }
    #[must_use]
    pub fn username(&self) -> Option<&str> {
        self.username.as_deref()
    }
    #[must_use]
    pub fn password(&self) -> Option<&str> {
        self.password.as_deref()
    }
    #[must_use]
    pub fn command(&self) -> Option<&str> {
        self.command.as_deref()
    }
}
impl Default for EmailConfig {
    fn default() -> Self {
        Self {
            from: default_email(),
            reply_to: default_email(),
            transport: EmailTransportKind::Blackhole,
            mode: None,
            hostname: None,
            port: None,
            username: None,
            password: None,
            command: None,
        }
    }
}
impl ConfigurationSection for EmailConfig {
    const PATH: Option<&'static str> = Some("email");
    fn validate(&self, figment: &figment::Figment) -> Result<(), figment::error::Error> {
        let metadata = figment.find_metadata(Self::PATH.unwrap());
        let error_on_field = |mut error: figment::error::Error, field: &'static str| {
            error.metadata = metadata.cloned();
            error.profile = Some(figment::Profile::Default);
            error.path = vec![Self::PATH.unwrap().to_owned(), field.to_owned()];
            error
        };
        let missing_field = |field: &'static str| {
            error_on_field(figment::error::Error::missing_field(field), field)
        };
        let unexpected_field = |field: &'static str, expected_fields: &'static [&'static str]| {
            error_on_field(
                figment::error::Error::unknown_field(field, expected_fields),
                field,
            )
        };
        match self.transport {
            EmailTransportKind::Blackhole => {}
            EmailTransportKind::Smtp => {
                if let Err(e) = Mailbox::from_str(&self.from) {
                    return Err(error_on_field(figment::error::Error::custom(e), "from"));
                }
                if let Err(e) = Mailbox::from_str(&self.reply_to) {
                    return Err(error_on_field(figment::error::Error::custom(e), "reply_to"));
                }
                match (self.username.is_some(), self.password.is_some()) {
                    (true, true) | (false, false) => {}
                    (true, false) => {
                        return Err(missing_field("password"));
                    }
                    (false, true) => {
                        return Err(missing_field("username"));
                    }
                }
                if self.mode.is_none() {
                    return Err(missing_field("mode"));
                }
                if self.hostname.is_none() {
                    return Err(missing_field("hostname"));
                }
                if self.command.is_some() {
                    return Err(unexpected_field(
                        "command",
                        &[
                            "from",
                            "reply_to",
                            "transport",
                            "mode",
                            "hostname",
                            "port",
                            "username",
                            "password",
                        ],
                    ));
                }
            }
            EmailTransportKind::Sendmail => {
                let expected_fields = &["from", "reply_to", "transport", "command"];
                if let Err(e) = Mailbox::from_str(&self.from) {
                    return Err(error_on_field(figment::error::Error::custom(e), "from"));
                }
                if let Err(e) = Mailbox::from_str(&self.reply_to) {
                    return Err(error_on_field(figment::error::Error::custom(e), "reply_to"));
                }
                if self.command.is_none() {
                    return Err(missing_field("command"));
                }
                if self.mode.is_some() {
                    return Err(unexpected_field("mode", expected_fields));
                }
                if self.hostname.is_some() {
                    return Err(unexpected_field("hostname", expected_fields));
                }
                if self.port.is_some() {
                    return Err(unexpected_field("port", expected_fields));
                }
                if self.username.is_some() {
                    return Err(unexpected_field("username", expected_fields));
                }
                if self.password.is_some() {
                    return Err(unexpected_field("password", expected_fields));
                }
            }
        }
        Ok(())
    }
}