//! Email service implementation
//!
//! This module provides the main EmailService struct that coordinates email sending
//! across different providers and handles template rendering, configuration, and error handling.

use crate::email::providers::EmailProvider as EmailProviderTrait;
use crate::email::{
    ConsoleProvider, EmailConfig, EmailError, EmailMessage, EmailProvider, EmailResult,
    EmailTemplateEngine, FormSubmission, SendGridProvider, SmtpProvider,
};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, error, info};

/// Main email service that coordinates email sending
pub struct EmailService {
    provider: Arc<dyn EmailProviderTrait>,
    template_engine: Arc<RwLock<EmailTemplateEngine>>,
    config: EmailConfig,
}

impl EmailService {
    /// Create a new email service with the given configuration
    pub async fn new(config: EmailConfig) -> EmailResult<Self> {
        // Create the appropriate provider based on configuration
        let provider: Arc<dyn EmailProviderTrait> = match config.provider {
            EmailProvider::Smtp => {
                let smtp_config = config.smtp.as_ref().ok_or_else(|| {
                    EmailError::config("SMTP configuration required when using SMTP provider")
                })?;
                Arc::new(SmtpProvider::new(smtp_config.clone())?)
            }
            EmailProvider::SendGrid => {
                let sendgrid_config = config.sendgrid.as_ref().ok_or_else(|| {
                    EmailError::config(
                        "SendGrid configuration required when using SendGrid provider",
                    )
                })?;
                Arc::new(SendGridProvider::new(sendgrid_config.clone())?)
            }
            EmailProvider::Console => Arc::new(ConsoleProvider::new()),
        };

        // Create template engine with default language
        let template_engine = EmailTemplateEngine::new_with_language(&config.template_dir, "en")?;
        let template_engine = Arc::new(RwLock::new(template_engine));

        info!(
            "Email service initialized with provider: {}",
            provider.provider_name()
        );

        Ok(Self {
            provider,
            template_engine,
            config,
        })
    }

    /// Send an email message
    pub async fn send_email(&self, message: &EmailMessage) -> EmailResult<String> {
        self.send_email_with_language(message, "en").await
    }

    /// Send an email message with specific language
    pub async fn send_email_with_language(
        &self,
        message: &EmailMessage,
        language: &str,
    ) -> EmailResult<String> {
        if !self.config.enabled {
            debug!(
                "Email sending is disabled, skipping message to: {}",
                message.to
            );
            return Ok("disabled".to_string());
        }

        // Validate the message
        self.validate_message(message)?;

        // Process the message (apply templates, set defaults, etc.)
        let processed_message = self
            .process_message_with_language(message, language)
            .await?;

        // Send the email
        debug!("Sending email to: {}", processed_message.to);
        let result = self.provider.send_email(&processed_message).await;

        match &result {
            Ok(message_id) => {
                info!(
                    "Email sent successfully to: {}, Message ID: {}",
                    processed_message.to, message_id
                );
            }
            Err(e) => {
                error!(
                    "Failed to send email to: {}, Error: {}",
                    processed_message.to, e
                );
            }
        }

        result
    }

    /// Send a form submission email
    pub async fn send_form_submission(
        &self,
        submission: &FormSubmission,
        recipient: &str,
    ) -> EmailResult<String> {
        self.send_form_submission_with_language(submission, recipient, "en")
            .await
    }

    /// Send a form submission email with specific language
    pub async fn send_form_submission_with_language(
        &self,
        submission: &FormSubmission,
        recipient: &str,
        language: &str,
    ) -> EmailResult<String> {
        let message = submission.to_email_message(recipient);
        self.send_email_with_language(&message, language).await
    }

    /// Send a simple text email
    pub async fn send_simple_email(
        &self,
        to: &str,
        subject: &str,
        body: &str,
    ) -> EmailResult<String> {
        self.send_simple_email_with_language(to, subject, body, "en")
            .await
    }

    /// Send a simple text email with specific language
    pub async fn send_simple_email_with_language(
        &self,
        to: &str,
        subject: &str,
        body: &str,
        language: &str,
    ) -> EmailResult<String> {
        let message = EmailMessage::new(to, subject)
            .text_body(body)
            .from(&self.config.default_from)
            .from_name(&self.config.default_from_name);

        self.send_email_with_language(&message, language).await
    }

    /// Send an email using a template
    pub async fn send_templated_email(
        &self,
        to: &str,
        subject: &str,
        template_name: &str,
        template_data: HashMap<String, serde_json::Value>,
    ) -> EmailResult<String> {
        self.send_templated_email_with_language(to, subject, template_name, template_data, "en")
            .await
    }

    /// Send an email using a template with specific language
    pub async fn send_templated_email_with_language(
        &self,
        to: &str,
        subject: &str,
        template_name: &str,
        template_data: HashMap<String, serde_json::Value>,
        language: &str,
    ) -> EmailResult<String> {
        let message = EmailMessage::new(to, subject)
            .template(template_name)
            .template_data_map(template_data)
            .from(&self.config.default_from)
            .from_name(&self.config.default_from_name);

        self.send_email_with_language(&message, language).await
    }

    /// Send a notification email
    pub async fn send_notification(
        &self,
        to: &str,
        title: &str,
        message: &str,
        content: Option<&str>,
    ) -> EmailResult<String> {
        self.send_notification_with_language(to, title, message, content, "en")
            .await
    }

    /// Send a notification email with specific language
    pub async fn send_notification_with_language(
        &self,
        to: &str,
        title: &str,
        message: &str,
        content: Option<&str>,
        language: &str,
    ) -> EmailResult<String> {
        let mut template_data = HashMap::new();
        template_data.insert(
            "title".to_string(),
            serde_json::Value::String(title.to_string()),
        );
        template_data.insert(
            "message".to_string(),
            serde_json::Value::String(message.to_string()),
        );

        if let Some(content) = content {
            template_data.insert(
                "content".to_string(),
                serde_json::Value::String(content.to_string()),
            );
        }

        self.send_templated_email_with_language(to, title, "notification", template_data, language)
            .await
    }

    /// Send a contact form submission
    pub async fn send_contact_form(
        &self,
        name: &str,
        email: &str,
        subject: &str,
        message: &str,
        recipient: &str,
    ) -> EmailResult<String> {
        self.send_contact_form_with_language(name, email, subject, message, recipient, "en")
            .await
    }

    /// Send a contact form submission with specific language
    pub async fn send_contact_form_with_language(
        &self,
        name: &str,
        email: &str,
        subject: &str,
        message: &str,
        recipient: &str,
        language: &str,
    ) -> EmailResult<String> {
        let submission = FormSubmission::new("contact", name, email, subject, message);
        self.send_form_submission_with_language(&submission, recipient, language)
            .await
    }

    /// Send a support form submission
    pub async fn send_support_form(
        &self,
        name: &str,
        email: &str,
        subject: &str,
        message: &str,
        priority: Option<&str>,
        category: Option<&str>,
        recipient: &str,
    ) -> EmailResult<String> {
        self.send_support_form_with_language(
            name, email, subject, message, priority, category, recipient, "en",
        )
        .await
    }

    /// Send a support form submission with specific language
    pub async fn send_support_form_with_language(
        &self,
        name: &str,
        email: &str,
        subject: &str,
        message: &str,
        priority: Option<&str>,
        category: Option<&str>,
        recipient: &str,
        language: &str,
    ) -> EmailResult<String> {
        let mut submission = FormSubmission::new("support", name, email, subject, message);

        if let Some(priority) = priority {
            submission = submission.field("priority", priority);
        }

        if let Some(category) = category {
            submission = submission.field("category", category);
        }

        self.send_form_submission_with_language(&submission, recipient, language)
            .await
    }

    /// Get email configuration
    pub fn get_config(&self) -> &EmailConfig {
        &self.config
    }

    /// Check if email service is enabled
    pub fn is_enabled(&self) -> bool {
        self.config.enabled
    }

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

    /// Check if provider is configured
    pub fn is_configured(&self) -> bool {
        self.provider.is_configured()
    }

    /// Reload email templates
    pub async fn reload_templates(&self) -> EmailResult<()> {
        let mut template_engine = self.template_engine.write().await;
        template_engine.reload_templates()?;
        info!("Email templates reloaded successfully");
        Ok(())
    }

    /// Get available template names
    pub async fn get_template_names(&self) -> Vec<String> {
        let template_engine = self.template_engine.read().await;
        template_engine.get_template_names()
    }

    /// Get available template languages
    pub async fn get_available_languages(&self) -> Vec<String> {
        let template_engine = self.template_engine.read().await;
        template_engine.get_available_languages()
    }

    /// Check if a template exists for a specific language
    pub async fn has_template_for_language(&self, template_name: &str, language: &str) -> bool {
        let template_engine = self.template_engine.read().await;
        template_engine.has_template_for_language(template_name, language)
    }

    /// Validate an email message
    fn validate_message(&self, message: &EmailMessage) -> EmailResult<()> {
        // Check recipient
        if message.to.is_empty() {
            return Err(EmailError::validation("Recipient email is required"));
        }

        // Basic email validation
        if !message.to.contains('@') {
            return Err(EmailError::validation("Invalid recipient email format"));
        }

        // Check subject
        if message.subject.is_empty() {
            return Err(EmailError::validation("Email subject is required"));
        }

        // Check that we have either a body or a template
        if message.text_body.is_none() && message.html_body.is_none() && message.template.is_none()
        {
            return Err(EmailError::validation(
                "Email must have either text body, HTML body, or template",
            ));
        }

        // Validate sender if provided
        if let Some(from) = &message.from {
            if !from.contains('@') {
                return Err(EmailError::validation("Invalid sender email format"));
            }
        }

        // Validate CC emails
        if let Some(cc_list) = &message.cc {
            for cc in cc_list {
                if !cc.contains('@') {
                    return Err(EmailError::validation(&format!(
                        "Invalid CC email format: {}",
                        cc
                    )));
                }
            }
        }

        // Validate BCC emails
        if let Some(bcc_list) = &message.bcc {
            for bcc in bcc_list {
                if !bcc.contains('@') {
                    return Err(EmailError::validation(&format!(
                        "Invalid BCC email format: {}",
                        bcc
                    )));
                }
            }
        }

        // Validate reply-to
        if let Some(reply_to) = &message.reply_to {
            if !reply_to.contains('@') {
                return Err(EmailError::validation("Invalid reply-to email format"));
            }
        }

        Ok(())
    }

    /// Process an email message (apply templates, set defaults, etc.)
    #[allow(dead_code)]
    async fn process_message(&self, message: &EmailMessage) -> EmailResult<EmailMessage> {
        self.process_message_with_language(message, "en").await
    }

    /// Process an email message with specific language (apply templates, set defaults, etc.)
    async fn process_message_with_language(
        &self,
        message: &EmailMessage,
        language: &str,
    ) -> EmailResult<EmailMessage> {
        let mut processed = message.clone();

        // Set default sender if not provided
        if processed.from.is_none() {
            processed.from = Some(self.config.default_from.clone());
        }

        if processed.from_name.is_none() {
            processed.from_name = Some(self.config.default_from_name.clone());
        }

        // Process template if specified
        if let Some(template_name) = &message.template {
            let empty_hashmap = HashMap::new();
            let template_data = message.template_data.as_ref().unwrap_or(&empty_hashmap);

            let template_engine_guard = self.template_engine.read().await;
            let template_engine = &*template_engine_guard;

            // Try to render HTML template
            let html_template_name = format!("{}_html", template_name);
            if template_engine.has_template_for_language(&html_template_name, language) {
                let html_body = template_engine.render_with_language(
                    &html_template_name,
                    template_data,
                    language,
                )?;
                processed.html_body = Some(html_body);
            }

            // Try to render text template
            let text_template_name = format!("{}_text", template_name);
            if template_engine.has_template_for_language(&text_template_name, language) {
                let text_body = template_engine.render_with_language(
                    &text_template_name,
                    template_data,
                    language,
                )?;
                processed.text_body = Some(text_body);
            }

            // If no templates found, return an error
            if processed.html_body.is_none() && processed.text_body.is_none() {
                return Err(EmailError::template(&format!(
                    "Template not found: {} for language {} (looked for {}_html and {}_text)",
                    template_name, language, template_name, template_name
                )));
            }
        }

        Ok(processed)
    }
}

/// Email service builder for easier configuration
pub struct EmailServiceBuilder {
    config: EmailConfig,
}

impl EmailServiceBuilder {
    /// Create a new email service builder
    pub fn new() -> Self {
        Self {
            config: EmailConfig {
                default_from: "noreply@example.com".to_string(),
                default_from_name: "No Reply".to_string(),
                provider: crate::email::EmailProvider::Console,
                smtp: None,
                sendgrid: None,
                template_dir: "./templates/email".to_string(),
                enabled: true,
            },
        }
    }

    /// Set default sender email
    pub fn default_from(mut self, email: &str) -> Self {
        self.config.default_from = email.to_string();
        self
    }

    /// Set default sender name
    pub fn default_from_name(mut self, name: &str) -> Self {
        self.config.default_from_name = name.to_string();
        self
    }

    /// Set provider to SMTP
    pub fn smtp_provider(mut self, smtp_config: crate::email::SmtpConfig) -> Self {
        self.config.provider = crate::email::EmailProvider::Smtp;
        self.config.smtp = Some(smtp_config);
        self
    }

    /// Set provider to SendGrid
    pub fn sendgrid_provider(mut self, sendgrid_config: crate::email::SendGridConfig) -> Self {
        self.config.provider = crate::email::EmailProvider::SendGrid;
        self.config.sendgrid = Some(sendgrid_config);
        self
    }

    /// Set provider to Console (for development)
    pub fn console_provider(mut self) -> Self {
        self.config.provider = crate::email::EmailProvider::Console;
        self
    }

    /// Set template directory
    pub fn template_dir(mut self, dir: &str) -> Self {
        self.config.template_dir = dir.to_string();
        self
    }

    /// Enable or disable email sending
    pub fn enabled(mut self, enabled: bool) -> Self {
        self.config.enabled = enabled;
        self
    }

    /// Build the email service
    pub async fn build(self) -> EmailResult<EmailService> {
        EmailService::new(self.config).await
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;

    #[tokio::test]
    async fn test_email_service_creation() {
        let temp_dir = TempDir::new().unwrap();

        let service = EmailServiceBuilder::new()
            .console_provider()
            .template_dir(temp_dir.path().to_str().unwrap())
            .build()
            .await;

        assert!(service.is_ok());

        let service = service.unwrap();
        assert_eq!(service.provider_name(), "Console");
        assert!(service.is_configured());
        assert!(service.is_enabled());
    }

    #[tokio::test]
    async fn test_simple_email_sending() {
        let temp_dir = TempDir::new().unwrap();

        let service = EmailServiceBuilder::new()
            .console_provider()
            .template_dir(temp_dir.path().to_str().unwrap())
            .default_from("test@example.com")
            .default_from_name("Test Sender")
            .build()
            .await
            .unwrap();

        let result = service
            .send_simple_email("recipient@example.com", "Test Subject", "Test message body")
            .await;

        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_contact_form_sending() {
        let temp_dir = TempDir::new().unwrap();

        let service = EmailServiceBuilder::new()
            .console_provider()
            .template_dir(temp_dir.path().to_str().unwrap())
            .enabled(false) // Disable email service for testing
            .build()
            .await
            .unwrap();

        let result = service
            .send_contact_form(
                "John Doe",
                "john@example.com",
                "Test Contact",
                "This is a test contact form message",
                "admin@example.com",
            )
            .await;

        // Should succeed because email service is disabled
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_notification_sending() {
        let temp_dir = TempDir::new().unwrap();

        let service = EmailServiceBuilder::new()
            .console_provider()
            .template_dir(temp_dir.path().to_str().unwrap())
            .enabled(false) // Disable email service for testing
            .build()
            .await
            .unwrap();

        let result = service
            .send_notification(
                "recipient@example.com",
                "Test Notification",
                "This is a test notification message",
                Some("Additional content here"),
            )
            .await;

        // Should succeed because email service is disabled
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_email_validation() {
        let temp_dir = TempDir::new().unwrap();

        let service = EmailServiceBuilder::new()
            .console_provider()
            .template_dir(temp_dir.path().to_str().unwrap())
            .build()
            .await
            .unwrap();

        // Test invalid email
        let invalid_message =
            EmailMessage::new("invalid-email", "Test Subject").text_body("Test body");

        let result = service.send_email(&invalid_message).await;
        assert!(result.is_err());

        // Test empty subject
        let empty_subject = EmailMessage::new("test@example.com", "").text_body("Test body");

        let result = service.send_email(&empty_subject).await;
        assert!(result.is_err());

        // Test no body or template
        let no_body = EmailMessage::new("test@example.com", "Test Subject");

        let result = service.send_email(&no_body).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_disabled_email_service() {
        let temp_dir = TempDir::new().unwrap();

        let service = EmailServiceBuilder::new()
            .console_provider()
            .template_dir(temp_dir.path().to_str().unwrap())
            .enabled(false)
            .build()
            .await
            .unwrap();

        let result = service
            .send_simple_email("test@example.com", "Test Subject", "Test body")
            .await;

        assert!(result.is_ok());
        assert_eq!(result.unwrap(), "disabled");
    }
}
