use async_graphql::{Context, Object, Result};
use std::collections::HashMap;
use std::sync::Arc;

use crate::graphql::types::{
    EmailTemplateType, SendEmailInput, SendEmailResult, SendTemplateEmailInput,
};
use crate::services::email_templates::EmailTemplateRegistry;
use crate::services::gmail::{GmailService, SendEmailRequest};

#[derive(Default)]
pub struct EmailMutation;

#[Object]
impl EmailMutation {
    /// Send an email
    async fn send_email(
        &self,
        ctx: &Context<'_>,
        input: SendEmailInput,
    ) -> Result<SendEmailResult> {
        let gmail = ctx.data::<Arc<GmailService>>()?;

        let request = SendEmailRequest {
            to: input.to,
            cc: input.cc,
            bcc: input.bcc,
            subject: input.subject,
            body: input.body,
            content_type: input.content_type,
            from_name: input.from_name,
            attachments: None,
        };

        match gmail.send_email(request).await {
            Ok(response) => Ok(SendEmailResult {
                success: true,
                message_id: Some(response.id),
                thread_id: Some(response.thread_id),
                error: None,
            }),
            Err(e) => Ok(SendEmailResult {
                success: false,
                message_id: None,
                thread_id: None,
                error: Some(e.to_string()),
            }),
        }
    }

    /// Send an email using a template
    async fn send_template_email(
        &self,
        ctx: &Context<'_>,
        input: SendTemplateEmailInput,
    ) -> Result<SendEmailResult> {
        let gmail = ctx.data::<Arc<GmailService>>()?;
        let registry = ctx.data::<EmailTemplateRegistry>()?;

        // Get the template
        let template = match registry.get(&input.template_id) {
            Some(t) => t,
            None => {
                return Ok(SendEmailResult {
                    success: false,
                    message_id: None,
                    thread_id: None,
                    error: Some(format!("Template not found: {}", input.template_id)),
                });
            }
        };

        // Convert variables to HashMap
        let variables: HashMap<String, String> = input
            .variables
            .into_iter()
            .map(|v| (v.name, v.value))
            .collect();

        // Render the template
        let (subject, body) = match template.render(&variables) {
            Ok(result) => result,
            Err(e) => {
                return Ok(SendEmailResult {
                    success: false,
                    message_id: None,
                    thread_id: None,
                    error: Some(e.to_string()),
                });
            }
        };

        // Send the email
        let request = SendEmailRequest {
            to: input.to,
            cc: input.cc,
            bcc: input.bcc,
            subject,
            body,
            content_type: Some(template.content_type.clone()),
            from_name: input.from_name,
            attachments: None,
        };

        match gmail.send_email(request).await {
            Ok(response) => Ok(SendEmailResult {
                success: true,
                message_id: Some(response.id),
                thread_id: Some(response.thread_id),
                error: None,
            }),
            Err(e) => Ok(SendEmailResult {
                success: false,
                message_id: None,
                thread_id: None,
                error: Some(e.to_string()),
            }),
        }
    }

    /// List all available email templates
    async fn list_email_templates(
        &self,
        ctx: &Context<'_>,
    ) -> Result<Vec<EmailTemplateType>> {
        let registry = ctx.data::<EmailTemplateRegistry>()?;

        let templates: Vec<EmailTemplateType> = registry
            .list()
            .into_iter()
            .map(EmailTemplateType::from)
            .collect();

        Ok(templates)
    }

    /// Get a specific email template by ID
    async fn get_email_template(
        &self,
        ctx: &Context<'_>,
        template_id: String,
    ) -> Result<Option<EmailTemplateType>> {
        let registry = ctx.data::<EmailTemplateRegistry>()?;

        Ok(registry.get(&template_id).map(EmailTemplateType::from))
    }
}
