use std::time::SystemTime;

use anyhow::Result;
use lettre::message::MessageBuilder;
use lettre::message::{Mailbox as LettreMailBox, header::ContentType};
use lettre::transport::smtp::authentication::Credentials;
use lettre::transport::smtp::client::Tls;
use lettre::{Message, SmtpTransport, Transport};
use once_cell::sync::OnceCell;

use crate::view::{ViewData, view_render};

pub(crate) static mut GLOBAL_MAILER: OnceCell<Box<SMTP>> = OnceCell::new();

pub(crate) struct SMTP {
    transport: SmtpTransport,
}

impl SMTP {
    pub fn new(host: String, port: u16, username: String, password: String, tls: bool) -> Result<SMTP> {
        let mut builder = SmtpTransport::relay(&host)
                .unwrap()
                .port(port)
                .credentials(Credentials::new(username, password));

        if !tls {
            builder = builder.tls(Tls::None);
        }

        return Ok(Self {
            transport: builder.build(),
        });
    }

    #[allow(static_mut_refs)]
    pub fn add(host: String, port: u16, username: String, password: String, tls: bool) -> Result<()> {
        unsafe { 
            return GLOBAL_MAILER
                .set(Box::new(Self::new(host, port, username, password, tls).unwrap()))
                .map_err(|_| anyhow::anyhow!("Global mailer already initialized"));
        }
    }
}

pub struct Mailbox {
    email: String,
    name: Option<String>
}

impl Mailbox {
    pub fn new(email: String, name: Option<String>) -> Self {
        return Self {
            email: email,
            name: name
        };
    }
}

pub struct Mail {
    builder: MessageBuilder,
    body: Option<String>,
}

// TODO: Need to implement (attachment, attachments)
impl Mail {
    pub fn new() -> Self {
        return Self {
            builder: Message::builder(),
            body: None,
        };
    }

    pub fn from(&mut self, email: String, name: Option<String>) -> &mut Self {
        self.builder = self.builder
            .clone()
            .from(LettreMailBox::new(name, email.parse().unwrap()));

        return self;
    }

    pub fn reply_to(&mut self, email: String, name: Option<String>) -> &mut Self {
        self.builder = self
            .builder
            .clone()
            .reply_to(LettreMailBox::new(name, email.parse().unwrap()));

        return self;
    }

    pub fn sender(&mut self, email: String, name: Option<String>) -> &mut Self {
        self.builder = self
            .builder
            .clone()
            .sender(LettreMailBox::new(name, email.parse().unwrap()));

        return self;
    }

    pub fn date(&mut self) -> &mut Self {
        self.builder = self
            .builder
            .clone()
            .date_now();

        return self;
    }

    pub fn date_now(&mut self, time: SystemTime) -> &mut Self {
        self.builder = self
            .builder
            .clone()
            .date(time);

        return self;
    }

    pub fn cc(&mut self, email: String, name: Option<String>) -> &mut Self {
        self.builder = self
            .builder
            .clone()
            .cc(LettreMailBox::new(name, email.parse().unwrap()));

        return self;
    }

    pub fn bcc(&mut self, email: String, name: Option<String>) -> &mut Self {
        self.builder = self
            .builder
            .clone()
            .bcc(LettreMailBox::new(name, email.parse().unwrap()));

        return self;
    }

    pub fn subject(&mut self, subject: String) -> &mut Self {
        self.builder = self.builder.clone().subject(subject);

        return self;
    }

    pub fn text(&mut self, text: String) -> &mut Self {
        self.builder = self.builder
            .clone()
            .header(ContentType::TEXT_PLAIN);

        self.body = Some(text);

        return self;
    }

    pub fn html(&mut self, html: String) -> &mut Self {
        self.builder = self.builder
            .clone()
            .header(ContentType::TEXT_HTML);

        self.body = Some(html);

        return self;
    }

    pub fn view(&mut self, view: String, data: Option<ViewData>) -> &mut Self {
        return self.html(view_render(&view, data).unwrap());
    }

    #[allow(static_mut_refs)]
    pub async fn send_to_many(&mut self, to: Vec<Mailbox>) -> Result<()> {
        unsafe {
            let transport = &GLOBAL_MAILER
                    .get_mut()
                    .unwrap()
                    .transport;

            for mailbox in to {
                let message =  self.builder
                    .clone()
                    .to(LettreMailBox::new(mailbox.name, mailbox.email.parse().unwrap()))
                    .body(self.body.clone().unwrap_or(String::new()))
                    .unwrap();

                transport.send(&message).unwrap();
            }

            return Ok(());
        }
    }

    pub async fn send(&mut self, email: String, name: Option<String>) -> Result<()> {
        return self.send_to_many(vec![Mailbox::new(email, name)]).await;
    }
}