use crate::types::MailServerConfig;
use lettre::message::{MultiPart, SinglePart};
use lettre::{Message, SmtpTransport, Transport};
use std::error::Error;

pub struct SmtpClient {
    config: MailServerConfig,
    email: String,
}

impl SmtpClient {
    pub fn new(config: MailServerConfig, email: String) -> Self {
        Self { config, email }
    }

    pub async fn send_mail(
        &self,
        password: &str,
        to: &str,
        subject: &str,
        body_text: &str,
        attachments: Vec<crate::types::Attachment>,
    ) -> Result<Vec<u8>, Box<dyn Error>> {
        let mut builder = Message::builder()
            .from(self.email.parse()?)
            .subject(subject);
        for addr in to.split(',') {
            builder = builder.to(addr.trim().parse()?);
        }

        let email = if attachments.is_empty() {
            builder.singlepart(SinglePart::plain(body_text.to_string()))?
        } else {
            let mut multipart =
                MultiPart::mixed().singlepart(SinglePart::plain(body_text.to_string()));

            for att in attachments {
                multipart = multipart.singlepart(
                    SinglePart::builder()
                        .header(lettre::message::header::ContentType::parse(
                            &att.content_type,
                        )?)
                        .header(lettre::message::header::ContentDisposition::attachment(
                            &att.name,
                        ))
                        .body(att.content),
                );
            }
            builder.multipart(multipart)?
        };

        use lettre::transport::smtp::client::{Tls, TlsParameters};

        let creds = lettre::transport::smtp::authentication::Credentials::new(
            self.email.clone(),
            password.to_string(),
        );

        let tls_parameters = TlsParameters::new(self.config.smtp_host.clone())?;

        // Explicitly choose TLS mode based on port
        let tls = if self.config.smtp_port == 465 {
            Tls::Wrapper(tls_parameters)
        } else {
            Tls::Opportunistic(tls_parameters)
        };

        let mailer = SmtpTransport::builder_dangerous(&self.config.smtp_host)
            .port(self.config.smtp_port)
            .tls(tls)
            .credentials(creds)
            .build();

        let raw_bytes = email.formatted();
        mailer.send(&email)?;

        Ok(raw_bytes)
    }

    pub async fn send_raw_mail(
        &self,
        password: &str,
        to: &str,
        _subject: &str,
        raw_message: &[u8],
    ) -> Result<(), Box<dyn Error>> {
        use lettre::transport::smtp::client::{Tls, TlsParameters};

        let creds = lettre::transport::smtp::authentication::Credentials::new(
            self.email.clone(),
            password.to_string(),
        );

        let tls_parameters = TlsParameters::new(self.config.smtp_host.clone())?;

        let tls = if self.config.smtp_port == 465 {
            Tls::Wrapper(tls_parameters)
        } else {
            Tls::Opportunistic(tls_parameters)
        };

        let mailer = SmtpTransport::builder_dangerous(&self.config.smtp_host)
            .port(self.config.smtp_port)
            .tls(tls)
            .credentials(creds)
            .build();

        // Build a manual message from raw bytes; `to` may be comma-separated
        let recipients: Vec<lettre::address::Address> = to
            .split(',')
            .map(|addr| addr.trim().parse())
            .collect::<Result<Vec<_>, _>>()?;
        let envelope =
            lettre::address::Envelope::new(Some(self.email.parse()?), recipients)?;

        mailer.send_raw(&envelope, raw_message)?;

        Ok(())
    }
}
