/*
 * authservice email/mod.rs
 * - email thread, most of everything needed for emails are in this file
 *
 * Copyright (C) 2025 Real Microsoft, LLC
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

mod templates;

use lettre::{Message, SmtpTransport, Transport};
use lettre::message::header::ContentType;
use lettre::transport::smtp::authentication::Credentials;
use log::{error, info};
use tokio::sync::{mpsc, OnceCell};
use crate::{SMTP_DISABLE, SMTP_PASSWORD, SMTP_URL, SMTP_USERNAME};

pub enum EmailMessage {
    VerificationCode {
        to: String,
        username: String,
        url: String,
    },
    DeveloperWarning {
        message: String,
    },
    Announcement {
        to: String,
        username: String,
        title: String,
        text_contents: String,
    }
}

static EMAIL_SENDER: OnceCell<mpsc::UnboundedSender<EmailMessage>> = OnceCell::const_new();
const EMAIL_RETRY_TIME_SECS: u64 = 10;
const DEVWARNING_TARGET: &str = "AskLyphe Developer <devnull@voremicrocomputers.com>";

pub fn send_devwarn_email(message: &str) {
    EMAIL_SENDER.get().unwrap().send(EmailMessage::DeveloperWarning {
        message: message.to_string(),
    }).unwrap();
}

pub fn send_verification_code_email(to: &str, username: &str, url: &str) {
    EMAIL_SENDER.get().unwrap().send(EmailMessage::VerificationCode {
        to: to.to_string(),
        username: username.to_string(),
        url: url.to_string(),
    }).unwrap();
}

pub fn send_announcement_email(to: &str, username: &str, title: &str, content: &str) {
    EMAIL_SENDER.get().unwrap().send(EmailMessage::Announcement {
        to: to.to_string(),
        username: username.to_string(),
        title: title.to_string(),
        text_contents: content.to_string(),
    }).unwrap()
}

pub async fn email_init() {
    let (email_sender, email_receiver) = mpsc::unbounded_channel();
    EMAIL_SENDER.set(email_sender).unwrap();

    tokio::spawn(async move {
        info!("email thread spawned");
        if *SMTP_DISABLE {
            info!("SMTP disable");
        }
        let creds = Credentials::new(SMTP_USERNAME.to_string(), SMTP_PASSWORD.to_string());
        let mailer = SmtpTransport::starttls_relay(&SMTP_URL).expect("FAILED TO CONNECT TO EMAIL SMTP RELAY")
            .credentials(creds)
            .build();
        let mut rx = email_receiver;

        loop {
            if let Ok(message) = rx.try_recv() {
                match message {
                    EmailMessage::VerificationCode { to, username, url } => {
                        if *SMTP_DISABLE {
                            info!("not sending smtp verification code due to smtp disable");
                            continue;
                        }
                        let body = templates::gen_verification_code_email(&url);
                        let email = Message::builder()
                            .from("Vore Microcomputers No-Reply <no-reply@voremicrocomputers.com>".parse().unwrap())
                            .reply_to("Vore Microcomputers Support <devnull@voremicrocomputers.com>".parse().unwrap());
                        let to_mbox = format!("{username} <{to}>").parse();
                        if let Err(e) = to_mbox {
                            error!("couldn't parse \"to\" field for verification code email! \"{username}\" \"{to}\"");
                            send_devwarn_email(&format!("couldn't parse \"to\" field for verification code email! \"{username}\" \"{to}\" you need to send the email manually and investigate"));
                            continue;
                        }
                        let email = email.to(to_mbox.unwrap())
                            .subject("AskLyphe Account Verification")
                            .header(ContentType::TEXT_PLAIN)
                            .body(body);
                        if let Err(e) = email {
                            error!("couldn't generate email for verification! \"{e}\" \"{username}\" \"{to}\"");
                            send_devwarn_email(&format!("couldn't generate verification code email! \"{e}\" \"{username}\" \"{to}\" you need to send the email manually and investigate"));
                            continue;
                        }
                        let email = email.unwrap();

                        if let Err(e) = mailer.send(&email) {
                            error!("couldn't send email for verification! \"{e}\" \"{username}\" \"{to}\"");
                            send_devwarn_email(&format!("couldn't send verification code email! \"{e}\" \"{username}\" \"{to}\" you need to send the email manually and investigate"));
                            continue;
                        }
                    }
                    EmailMessage::DeveloperWarning { message } => {
                        if *SMTP_DISABLE {
                            info!("not sending smtp developer warning due to smtp disable");
                            continue;
                        }
                        let body = templates::gen_developer_warning_email(&message);
                        let email = Message::builder()
                            .from("Vore Microcomputers No-Reply <no-reply@voremicrocomputers.com>".parse().unwrap())
                            .to(DEVWARNING_TARGET.parse().unwrap())
                            .subject("AskLyphe Developer Warning")
                            .header(ContentType::TEXT_PLAIN)
                            .body(body);
                        if let Err(e) = email {
                            error!("couldn't generate email for developer warning!! \"{e}\" \"{message}\"");
                            continue;
                        }
                        let email = email.unwrap();

                        if let Err(e) = mailer.send(&email) {
                            error!("couldn't send email for developer warning!!! \"{e}\" \"{message}\"");
                            continue;
                        }
                    }
                    EmailMessage::Announcement { to, username, title, text_contents } => {
                        if *SMTP_DISABLE {
                            info!("not sending smtp announcement due to smtp disable");
                            continue;
                        }
                        let body = templates::gen_announcement_email(&text_contents);
                        let email = Message::builder()
                            .from("Vore Microcomputers No-Reply <no-reply@voremicrocomputers.com>".parse().unwrap())
                            .reply_to("Vore Microcomputers Support <devnull@voremicrocomputers.com>".parse().unwrap());
                        let to_mbox = format!("{username} <{to}>").parse();
                        if let Err(e) = to_mbox {
                            error!("couldn't parse \"to\" field for announcement email! \"{username}\" \"{to}\"");
                            send_devwarn_email(&format!("couldn't parse \"to\" field for announcement email! \"{username}\" \"{to}\" you need to send the email manually and investigate"));
                            continue;
                        }
                        let email = email.to(to_mbox.unwrap())
                            .subject(format!("AskLyphe Announcement: {title}").as_str())
                            .header(ContentType::TEXT_PLAIN)
                            .body(body);
                        if let Err(e) = email {
                            error!("couldn't generate email for announcement! \"{e}\" \"{username}\" \"{to}\"");
                            send_devwarn_email(&format!("couldn't generate announcement email! \"{e}\" \"{username}\" \"{to}\" you need to send the email manually and investigate"));
                            continue;
                        }
                        let email = email.unwrap();

                        if let Err(e) = mailer.send(&email) {
                            error!("couldn't send email for announcement! \"{e}\" \"{username}\" \"{to}\"");
                            send_devwarn_email(&format!("couldn't send announcement email! \"{e}\" \"{username}\" \"{to}\" you need to send the email manually and investigate"));
                            continue;
                        }
                    }
                }
            }
        }
    });
}
