//! Email providers implementation
//!
//! This module implements different email providers including SMTP and SendGrid.
//! It provides a unified interface for sending emails regardless of the underlying provider.

use crate::email::{EmailError, EmailMessage, EmailResult, SendGridConfig, SmtpConfig};
use async_trait::async_trait;
use lettre::message::{Mailbox, Message, MultiPart, SinglePart, header};
use lettre::transport::smtp::authentication::Credentials;
use lettre::transport::smtp::client::{Tls, TlsParameters};
use lettre::{SmtpTransport, Transport};
use reqwest::Client;
use serde_json::json;
use std::collections::HashMap;
use tracing::{debug, error, info};

/// Trait for email providers
#[async_trait]
pub trait EmailProvider: Send + Sync {
    /// Send an email message
    async fn send_email(&self, message: &EmailMessage) -> EmailResult<String>;

    /// Check if the provider is configured and ready
    fn is_configured(&self) -> bool;

    /// Get provider name
    fn provider_name(&self) -> &'static str;
}

/// SMTP email provider
pub struct SmtpProvider {
    config: SmtpConfig,
    transport: Option<SmtpTransport>,
}

impl SmtpProvider {
    /// Create a new SMTP provider
    pub fn new(config: SmtpConfig) -> EmailResult<Self> {
        let transport = Self::create_transport(&config)?;

        Ok(Self {
            config,
            transport: Some(transport),
        })
    }

    /// Create SMTP transport
    fn create_transport(config: &SmtpConfig) -> EmailResult<SmtpTransport> {
        let mut builder = SmtpTransport::builder_dangerous(&config.host)
            .port(config.port)
            .credentials(Credentials::new(
                config.username.clone(),
                config.password.clone(),
            ));

        // Configure TLS
        if config.use_tls {
            let tls_parameters = TlsParameters::builder(config.host.clone())
                .build()
                .map_err(|e| {
                    EmailError::smtp(&format!("Failed to create TLS parameters: {}", e))
                })?;

            builder = builder.tls(Tls::Required(tls_parameters));
        } else if config.use_starttls {
            let tls_parameters = TlsParameters::builder(config.host.clone())
                .build()
                .map_err(|e| {
                    EmailError::smtp(&format!("Failed to create TLS parameters: {}", e))
                })?;

            builder = builder.tls(Tls::Wrapper(tls_parameters));
        } else {
            builder = builder.tls(Tls::None);
        }

        let transport = builder.build();

        Ok(transport)
    }

    /// Convert EmailMessage to lettre Message
    fn convert_message(&self, email_message: &EmailMessage) -> EmailResult<Message> {
        // Parse recipient
        let to: Mailbox = email_message
            .to
            .parse()
            .map_err(|e| EmailError::validation(&format!("Invalid recipient email: {}", e)))?;

        // Parse sender
        let from_email = email_message.from.as_ref().unwrap_or(&self.config.username);
        let from: Mailbox = if let Some(from_name) = &email_message.from_name {
            format!("{} <{}>", from_name, from_email)
        } else {
            from_email.clone()
        }
        .parse()
        .map_err(|e| EmailError::validation(&format!("Invalid sender email: {}", e)))?;

        // Start building the message
        let mut message_builder = Message::builder()
            .from(from)
            .to(to)
            .subject(&email_message.subject);

        // Add CC recipients
        if let Some(cc_list) = &email_message.cc {
            for cc in cc_list {
                let cc_mailbox: Mailbox = cc
                    .parse()
                    .map_err(|e| EmailError::validation(&format!("Invalid CC email: {}", e)))?;
                message_builder = message_builder.cc(cc_mailbox);
            }
        }

        // Add BCC recipients
        if let Some(bcc_list) = &email_message.bcc {
            for bcc in bcc_list {
                let bcc_mailbox: Mailbox = bcc
                    .parse()
                    .map_err(|e| EmailError::validation(&format!("Invalid BCC email: {}", e)))?;
                message_builder = message_builder.bcc(bcc_mailbox);
            }
        }

        // Add reply-to
        if let Some(reply_to) = &email_message.reply_to {
            let reply_to_mailbox: Mailbox = reply_to
                .parse()
                .map_err(|e| EmailError::validation(&format!("Invalid reply-to email: {}", e)))?;
            message_builder = message_builder.reply_to(reply_to_mailbox);
        }

        // Add custom headers - temporarily disabled due to API compatibility issues
        // TODO: Fix lettre header API usage
        if let Some(_headers) = &email_message.headers {
            // Custom headers temporarily disabled
        }

        // Build message body
        let message = match (&email_message.text_body, &email_message.html_body) {
            (Some(text), Some(html)) => {
                // Both text and HTML - create multipart
                let multipart = MultiPart::alternative()
                    .singlepart(
                        SinglePart::builder()
                            .header(header::ContentType::TEXT_PLAIN)
                            .body(text.clone()),
                    )
                    .singlepart(
                        SinglePart::builder()
                            .header(header::ContentType::TEXT_HTML)
                            .body(html.clone()),
                    );

                message_builder.multipart(multipart).map_err(|e| {
                    EmailError::smtp(&format!("Failed to build multipart message: {}", e))
                })?
            }
            (Some(text), None) => {
                // Text only
                message_builder.body(text.clone()).map_err(|e| {
                    EmailError::smtp(&format!("Failed to build text message: {}", e))
                })?
            }
            (None, Some(html)) => {
                // HTML only
                message_builder
                    .singlepart(
                        SinglePart::builder()
                            .header(header::ContentType::TEXT_HTML)
                            .body(html.clone()),
                    )
                    .map_err(|e| {
                        EmailError::smtp(&format!("Failed to build HTML message: {}", e))
                    })?
            }
            (None, None) => {
                return Err(EmailError::validation(
                    "Email message must have either text or HTML body",
                ));
            }
        };

        Ok(message)
    }
}

#[async_trait]
impl EmailProvider for SmtpProvider {
    async fn send_email(&self, message: &EmailMessage) -> EmailResult<String> {
        let transport = self
            .transport
            .as_ref()
            .ok_or_else(|| EmailError::smtp("SMTP transport not initialized"))?;

        let lettre_message = self.convert_message(message)?;

        debug!("Sending email via SMTP to: {}", message.to);

        let result = transport.send(&lettre_message);

        match result {
            Ok(response) => {
                let message_id = format!("smtp-{}", response.code());
                debug!(
                    "Email sent successfully via SMTP. Response: {}",
                    response.message().collect::<Vec<_>>().join(" ")
                );
                Ok(message_id)
            }
            Err(e) => {
                error!("Failed to send email via SMTP: {}", e);
                Err(EmailError::smtp(&format!("SMTP send failed: {}", e)))
            }
        }
    }

    fn is_configured(&self) -> bool {
        self.transport.is_some()
            && !self.config.host.is_empty()
            && !self.config.username.is_empty()
            && !self.config.password.is_empty()
    }

    fn provider_name(&self) -> &'static str {
        "SMTP"
    }
}

/// SendGrid email provider
pub struct SendGridProvider {
    config: SendGridConfig,
    client: Client,
}

impl SendGridProvider {
    /// Create a new SendGrid provider
    pub fn new(config: SendGridConfig) -> EmailResult<Self> {
        let client = Client::new();

        Ok(Self { config, client })
    }

    /// Convert EmailMessage to SendGrid API format
    fn convert_to_sendgrid_format(&self, message: &EmailMessage) -> serde_json::Value {
        let mut personalizations = json!([{
            "to": [{"email": message.to}]
        }]);

        // Add recipient name if available
        if let Some(to_name) = &message.to_name {
            personalizations[0]["to"][0]["name"] = json!(to_name);
        }

        // Add CC recipients
        if let Some(cc_list) = &message.cc {
            let cc_array: Vec<serde_json::Value> = cc_list
                .iter()
                .map(|email| json!({"email": email}))
                .collect();
            personalizations[0]["cc"] = json!(cc_array);
        }

        // Add BCC recipients
        if let Some(bcc_list) = &message.bcc {
            let bcc_array: Vec<serde_json::Value> = bcc_list
                .iter()
                .map(|email| json!({"email": email}))
                .collect();
            personalizations[0]["bcc"] = json!(bcc_array);
        }

        // Build the main message structure
        let mut sendgrid_message = json!({
            "personalizations": personalizations,
            "subject": message.subject,
            "from": {"email": message.from.as_ref().unwrap_or(&"noreply@example.com".to_string())}
        });

        // Add sender name
        if let Some(from_name) = &message.from_name {
            sendgrid_message["from"]["name"] = json!(from_name);
        }

        // Add reply-to
        if let Some(reply_to) = &message.reply_to {
            sendgrid_message["reply_to"] = json!({"email": reply_to});
        }

        // Add content
        let mut content = Vec::new();

        if let Some(text_body) = &message.text_body {
            content.push(json!({
                "type": "text/plain",
                "value": text_body
            }));
        }

        if let Some(html_body) = &message.html_body {
            content.push(json!({
                "type": "text/html",
                "value": html_body
            }));
        }

        sendgrid_message["content"] = json!(content);

        // Add custom headers
        if let Some(headers) = &message.headers {
            let mut custom_headers = HashMap::new();
            for (key, value) in headers {
                custom_headers.insert(key, value);
            }
            sendgrid_message["headers"] = json!(custom_headers);
        }

        sendgrid_message
    }
}

#[async_trait]
impl EmailProvider for SendGridProvider {
    async fn send_email(&self, message: &EmailMessage) -> EmailResult<String> {
        let sendgrid_message = self.convert_to_sendgrid_format(message);

        debug!("Sending email via SendGrid to: {}", message.to);

        let response = self
            .client
            .post(&self.config.endpoint)
            .header("Authorization", format!("Bearer {}", self.config.api_key))
            .header("Content-Type", "application/json")
            .json(&sendgrid_message)
            .send()
            .await?;

        let status = response.status();
        let response_text = response.text().await?;

        if status.is_success() {
            // SendGrid returns 202 Accepted on success
            let message_id = format!("sendgrid-{}", chrono::Utc::now().timestamp());
            info!("Email sent successfully via SendGrid. Status: {}", status);
            Ok(message_id)
        } else {
            error!("SendGrid API error: {} - {}", status, response_text);
            Err(EmailError::sendgrid(&format!(
                "SendGrid API error: {} - {}",
                status, response_text
            )))
        }
    }

    fn is_configured(&self) -> bool {
        !self.config.api_key.is_empty() && !self.config.endpoint.is_empty()
    }

    fn provider_name(&self) -> &'static str {
        "SendGrid"
    }
}

/// Console email provider for development/testing
#[allow(dead_code)]
pub struct ConsoleProvider {
    name: String,
}

impl ConsoleProvider {
    /// Create a new console provider
    pub fn new() -> Self {
        Self {
            name: "Console".to_string(),
        }
    }
}

impl Default for ConsoleProvider {
    fn default() -> Self {
        Self::new()
    }
}

#[async_trait]
impl EmailProvider for ConsoleProvider {
    async fn send_email(&self, message: &EmailMessage) -> EmailResult<String> {
        println!("\n========== EMAIL MESSAGE ==========");
        println!("To: {}", message.to);
        if let Some(to_name) = &message.to_name {
            println!("To Name: {}", to_name);
        }
        if let Some(from) = &message.from {
            println!("From: {}", from);
        }
        if let Some(from_name) = &message.from_name {
            println!("From Name: {}", from_name);
        }
        if let Some(reply_to) = &message.reply_to {
            println!("Reply-To: {}", reply_to);
        }
        if let Some(cc) = &message.cc {
            println!("CC: {}", cc.join(", "));
        }
        if let Some(bcc) = &message.bcc {
            println!("BCC: {}", bcc.join(", "));
        }
        println!("Subject: {}", message.subject);

        if let Some(text_body) = &message.text_body {
            println!("\n--- TEXT BODY ---");
            println!("{}", text_body);
        }

        if let Some(html_body) = &message.html_body {
            println!("\n--- HTML BODY ---");
            println!("{}", html_body);
        }

        if let Some(headers) = &message.headers {
            println!("\n--- CUSTOM HEADERS ---");
            for (key, value) in headers {
                println!("{}: {}", key, value);
            }
        }

        println!("==================================\n");

        let message_id = format!("console-{}", chrono::Utc::now().timestamp());
        info!(
            "Email 'sent' via console provider. Message ID: {}",
            message_id
        );
        Ok(message_id)
    }

    fn is_configured(&self) -> bool {
        true // Console provider is always configured
    }

    fn provider_name(&self) -> &'static str {
        "Console"
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::email::EmailMessage;

    #[tokio::test]
    async fn test_console_provider() {
        let provider = ConsoleProvider::new();
        assert!(provider.is_configured());
        assert_eq!(provider.provider_name(), "Console");

        let message = EmailMessage::new("test@example.com", "Test Subject")
            .text_body("Test message body")
            .from("sender@example.com")
            .from_name("Test Sender");

        let result = provider.send_email(&message).await;
        assert!(result.is_ok());
    }

    #[test]
    fn test_smtp_provider_creation() {
        let config = SmtpConfig {
            host: "smtp.gmail.com".to_string(),
            port: 587,
            username: "test@gmail.com".to_string(),
            password: "password".to_string(),
            use_tls: false,
            use_starttls: true,
        };

        let provider = SmtpProvider::new(config);
        assert!(provider.is_ok());

        let provider = provider.unwrap();
        assert!(provider.is_configured());
        assert_eq!(provider.provider_name(), "SMTP");
    }

    #[test]
    fn test_sendgrid_provider_creation() {
        let config = SendGridConfig {
            api_key: "test-api-key".to_string(),
            endpoint: "https://api.sendgrid.com/v3/mail/send".to_string(),
        };

        let provider = SendGridProvider::new(config);
        assert!(provider.is_ok());

        let provider = provider.unwrap();
        assert!(provider.is_configured());
        assert_eq!(provider.provider_name(), "SendGrid");
    }

    #[test]
    fn test_sendgrid_message_conversion() {
        let config = SendGridConfig {
            api_key: "test-api-key".to_string(),
            endpoint: "https://api.sendgrid.com/v3/mail/send".to_string(),
        };

        let provider = SendGridProvider::new(config).unwrap();

        let message = EmailMessage::new("recipient@example.com", "Test Subject")
            .to_name("Recipient Name")
            .from("sender@example.com")
            .from_name("Sender Name")
            .text_body("Plain text body")
            .html_body("<p>HTML body</p>")
            .reply_to("reply@example.com")
            .cc("cc@example.com")
            .bcc("bcc@example.com");

        let sendgrid_json = provider.convert_to_sendgrid_format(&message);

        assert_eq!(sendgrid_json["subject"], "Test Subject");
        assert_eq!(sendgrid_json["from"]["email"], "sender@example.com");
        assert_eq!(sendgrid_json["from"]["name"], "Sender Name");
        assert_eq!(
            sendgrid_json["personalizations"][0]["to"][0]["email"],
            "recipient@example.com"
        );
        assert_eq!(
            sendgrid_json["personalizations"][0]["to"][0]["name"],
            "Recipient Name"
        );
        assert_eq!(sendgrid_json["reply_to"]["email"], "reply@example.com");

        // Check content
        let content = &sendgrid_json["content"];
        assert!(content.is_array());
        assert_eq!(content.as_array().unwrap().len(), 2);
    }
}
