//! Gmail API service for sending emails

use base64::{Engine as _, engine::general_purpose};
use reqwest::{Client, Response};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::Arc;
use thiserror::Error;

use super::google_auth::{GoogleAuthService, GMAIL_SEND_SCOPE, GMAIL_FULL_SCOPE};

const GMAIL_API_BASE: &str = "https://gmail.googleapis.com/gmail/v1";

#[derive(Debug, Error)]
pub enum GmailError {
    #[error("Authentication failed: {0}")]
    Auth(#[from] super::google_auth::GoogleAuthError),
    #[error("Request failed: {0}")]
    Request(String),
    #[error("Failed to parse response: {0}")]
    Parse(String),
    #[error("Email not found")]
    NotFound,
    #[error("Gmail API error: {0}")]
    ApiError(String),
    #[error("Gmail user not configured")]
    NotConfigured,
}

/// Email header
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailHeader {
    pub name: String,
    pub value: String,
}

/// Email body
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailBody {
    #[serde(rename = "attachmentId")]
    pub attachment_id: Option<String>,
    pub size: u64,
    pub data: Option<String>,
}

/// Email payload (MIME structure)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailPayload {
    #[serde(rename = "partId")]
    pub part_id: Option<String>,
    #[serde(rename = "mimeType")]
    pub mime_type: String,
    pub filename: Option<String>,
    pub headers: Vec<EmailHeader>,
    pub body: EmailBody,
    pub parts: Option<Vec<EmailPayload>>,
}

/// Full email message
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Email {
    pub id: String,
    #[serde(rename = "threadId")]
    pub thread_id: String,
    #[serde(rename = "labelIds")]
    pub label_ids: Vec<String>,
    pub snippet: String,
    pub payload: EmailPayload,
    #[serde(rename = "sizeEstimate")]
    pub size_estimate: u64,
    #[serde(rename = "historyId")]
    pub history_id: String,
    #[serde(rename = "internalDate")]
    pub internal_date: String,
}

/// Minimal email reference (for list results)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailMessage {
    pub id: String,
    #[serde(rename = "threadId")]
    pub thread_id: String,
}

/// Email list response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailListResponse {
    #[serde(default)]
    pub messages: Vec<EmailMessage>,
    #[serde(rename = "nextPageToken")]
    pub next_page_token: Option<String>,
    #[serde(rename = "resultSizeEstimate")]
    pub result_size_estimate: u32,
}

/// Email attachment
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailAttachment {
    pub filename: String,
    /// Base64 encoded content
    pub content: String,
    pub content_type: String,
}

/// Request to send an email
#[derive(Debug, Clone)]
pub struct SendEmailRequest {
    /// Recipient email addresses
    pub to: Vec<String>,
    /// CC recipients
    pub cc: Option<Vec<String>>,
    /// BCC recipients
    pub bcc: Option<Vec<String>>,
    /// Email subject
    pub subject: String,
    /// Email body
    pub body: String,
    /// Content type (defaults to "text/html")
    pub content_type: Option<String>,
    /// Display name for the sender
    pub from_name: Option<String>,
    /// Attachments
    pub attachments: Option<Vec<EmailAttachment>>,
}

/// Response from sending an email
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendEmailResponse {
    pub id: String,
    #[serde(rename = "threadId")]
    pub thread_id: String,
    #[serde(rename = "labelIds")]
    pub label_ids: Option<Vec<String>>,
}

/// Query for listing emails
#[derive(Debug, Clone, Default)]
pub struct ListEmailsQuery {
    /// Gmail search query
    pub q: Option<String>,
    /// Maximum results to return
    pub max_results: Option<u32>,
    /// Page token for pagination
    pub page_token: Option<String>,
    /// Filter by label IDs
    pub label_ids: Option<Vec<String>>,
    /// Include spam and trash
    pub include_spam_trash: Option<bool>,
}

/// Gmail service for sending and managing emails
pub struct GmailService {
    client: Client,
    auth: Arc<GoogleAuthService>,
    /// Email address to impersonate (domain-wide delegation)
    user_email: String,
}

impl GmailService {
    /// Create a new Gmail service
    pub fn new(auth: Arc<GoogleAuthService>, user_email: String) -> Self {
        Self {
            client: Client::new(),
            auth,
            user_email,
        }
    }

    /// Send an email
    pub async fn send_email(&self, request: SendEmailRequest) -> Result<SendEmailResponse, GmailError> {
        let url = format!("{}/users/me/messages/send", GMAIL_API_BASE);
        let raw_email = build_raw_email(&self.user_email, request)?;

        let body = json!({
            "raw": raw_email
        });

        let response = self.authenticated_post(&url, &body, GMAIL_SEND_SCOPE).await?;
        handle_send_response(response).await
    }

    /// List emails
    pub async fn list_emails(&self, query: ListEmailsQuery) -> Result<EmailListResponse, GmailError> {
        let url = build_list_emails_url(query);
        let response = self.authenticated_get(&url, GMAIL_FULL_SCOPE).await?;
        handle_list_response(response).await
    }

    /// Get a specific email
    pub async fn get_email(&self, email_id: &str) -> Result<Email, GmailError> {
        let url = format!("{}/users/me/messages/{}", GMAIL_API_BASE, urlencoding::encode(email_id));
        let response = self.authenticated_get(&url, GMAIL_FULL_SCOPE).await?;
        handle_email_response(response).await
    }

    /// Delete an email
    pub async fn delete_email(&self, email_id: &str) -> Result<(), GmailError> {
        let url = format!("{}/users/me/messages/{}", GMAIL_API_BASE, urlencoding::encode(email_id));
        let response = self.authenticated_delete(&url).await?;
        handle_delete_response(response).await
    }

    /// Mark an email as read
    pub async fn mark_as_read(&self, email_id: &str) -> Result<Email, GmailError> {
        let url = format!("{}/users/me/messages/{}/modify", GMAIL_API_BASE, urlencoding::encode(email_id));
        let body = json!({
            "removeLabelIds": ["UNREAD"]
        });
        let response = self.authenticated_post(&url, &body, GMAIL_FULL_SCOPE).await?;
        handle_email_response(response).await
    }

    /// Mark an email as unread
    pub async fn mark_as_unread(&self, email_id: &str) -> Result<Email, GmailError> {
        let url = format!("{}/users/me/messages/{}/modify", GMAIL_API_BASE, urlencoding::encode(email_id));
        let body = json!({
            "addLabelIds": ["UNREAD"]
        });
        let response = self.authenticated_post(&url, &body, GMAIL_FULL_SCOPE).await?;
        handle_email_response(response).await
    }

    /// Get the user email address
    pub fn user_email(&self) -> &str {
        &self.user_email
    }

    // Private helpers

    async fn authenticated_get(&self, url: &str, scope: &str) -> Result<Response, GmailError> {
        let token = self.auth.get_access_token(scope, Some(&self.user_email)).await?;
        self.client
            .get(url)
            .header("Authorization", format!("Bearer {}", token))
            .send()
            .await
            .map_err(|e| GmailError::Request(e.to_string()))
    }

    async fn authenticated_post(&self, url: &str, body: &serde_json::Value, scope: &str) -> Result<Response, GmailError> {
        let token = self.auth.get_access_token(scope, Some(&self.user_email)).await?;
        self.client
            .post(url)
            .header("Authorization", format!("Bearer {}", token))
            .header("Content-Type", "application/json")
            .json(body)
            .send()
            .await
            .map_err(|e| GmailError::Request(e.to_string()))
    }

    async fn authenticated_delete(&self, url: &str) -> Result<Response, GmailError> {
        let token = self.auth.get_access_token(GMAIL_FULL_SCOPE, Some(&self.user_email)).await?;
        self.client
            .delete(url)
            .header("Authorization", format!("Bearer {}", token))
            .send()
            .await
            .map_err(|e| GmailError::Request(e.to_string()))
    }
}

// Helper functions

fn build_raw_email(from_email: &str, request: SendEmailRequest) -> Result<String, GmailError> {
    let mut lines = Vec::new();

    // To header
    lines.push(format!("To: {}", request.to.join(", ")));

    // From header with optional display name
    if let Some(ref from_name) = request.from_name {
        lines.push(format!("From: \"{}\" <{}>", from_name, from_email));
    } else {
        lines.push(format!("From: {}", from_email));
    }

    // CC
    if let Some(cc) = request.cc {
        if !cc.is_empty() {
            lines.push(format!("Cc: {}", cc.join(", ")));
        }
    }

    // BCC
    if let Some(bcc) = request.bcc {
        if !bcc.is_empty() {
            lines.push(format!("Bcc: {}", bcc.join(", ")));
        }
    }

    // Subject
    lines.push(format!("Subject: {}", request.subject));

    // Content type
    let content_type = request.content_type.as_deref().unwrap_or("text/html");
    lines.push(format!("Content-Type: {}; charset=utf-8", content_type));

    // Empty line to separate headers from body
    lines.push(String::new());

    // Body
    lines.push(request.body);

    let raw = lines.join("\r\n");

    // Base64 encode using URL-safe alphabet
    Ok(general_purpose::URL_SAFE_NO_PAD.encode(raw.as_bytes()))
}

fn build_list_emails_url(query: ListEmailsQuery) -> String {
    let mut url = format!("{}/users/me/messages", GMAIL_API_BASE);
    let mut params = Vec::new();

    if let Some(q) = query.q {
        params.push(format!("q={}", urlencoding::encode(&q)));
    }
    if let Some(max_results) = query.max_results {
        params.push(format!("maxResults={}", max_results));
    }
    if let Some(page_token) = query.page_token {
        params.push(format!("pageToken={}", urlencoding::encode(&page_token)));
    }
    if let Some(label_ids) = query.label_ids {
        for label_id in label_ids {
            params.push(format!("labelIds={}", urlencoding::encode(&label_id)));
        }
    }
    if let Some(include_spam_trash) = query.include_spam_trash {
        params.push(format!("includeSpamTrash={}", include_spam_trash));
    }

    if !params.is_empty() {
        url.push('?');
        url.push_str(&params.join("&"));
    }

    url
}

async fn handle_send_response(response: Response) -> Result<SendEmailResponse, GmailError> {
    if response.status().is_success() {
        response
            .json()
            .await
            .map_err(|e| GmailError::Parse(e.to_string()))
    } else {
        Err(handle_error_response(response).await)
    }
}

async fn handle_email_response(response: Response) -> Result<Email, GmailError> {
    if response.status().is_success() {
        response
            .json()
            .await
            .map_err(|e| GmailError::Parse(e.to_string()))
    } else {
        Err(handle_error_response(response).await)
    }
}

async fn handle_list_response(response: Response) -> Result<EmailListResponse, GmailError> {
    if response.status().is_success() {
        response
            .json()
            .await
            .map_err(|e| GmailError::Parse(e.to_string()))
    } else {
        Err(handle_error_response(response).await)
    }
}

async fn handle_delete_response(response: Response) -> Result<(), GmailError> {
    match response.status().as_u16() {
        204 => Ok(()),
        404 => Err(GmailError::NotFound),
        _ => Err(handle_error_response(response).await),
    }
}

async fn handle_error_response(response: Response) -> GmailError {
    let status = response.status();
    let error_text = response
        .text()
        .await
        .unwrap_or_else(|_| "Unknown error".to_string());

    match status.as_u16() {
        404 => GmailError::NotFound,
        _ => GmailError::ApiError(format!("HTTP {}: {}", status, error_text)),
    }
}
