use async_trait::async_trait;
use serde::{Deserialize, Serialize};

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

#[derive(Debug, Serialize, Deserialize)]
pub struct GmailMessage {
    pub id: String,
    #[serde(rename = "threadId")]
    pub thread_id: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GmailMessageListResponse {
    pub messages: Option<Vec<GmailMessage>>,
    pub next_page_token: Option<String>,
    pub result_size_estimate: Option<u32>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GmailMessageHeader {
    pub name: String,
    pub value: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GmailMessagePart {
    #[serde(rename = "mimeType")]
    pub mime_type: Option<String>,
    pub filename: Option<String>,
    pub headers: Option<Vec<GmailMessageHeader>>,
    pub body: Option<GmailMessageBody>,
    pub parts: Option<Vec<GmailMessagePart>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GmailMessageBody {
    pub data: Option<String>,
    pub size: Option<u32>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GmailFullMessage {
    pub id: String,
    #[serde(rename = "threadId")]
    pub thread_id: String,
    pub label_ids: Option<Vec<String>>,
    pub snippet: Option<String>,
    pub payload: Option<GmailMessagePart>,
    pub internal_date: Option<String>,
}

#[derive(Debug, Clone)]
pub struct ParsedEmail {
    pub provider_id: String,
    pub subject: Option<String>,
    pub from_email: Option<String>,
    pub from_name: Option<String>,
    pub received_at: Option<String>,
    pub body_html: Option<String>,
    pub body_text: Option<String>,
    pub snippet: Option<String>,
}

#[async_trait]
pub trait TokenProvider: Send + Sync {
    async fn get_access_token(&self) -> Result<String, String>;
}

pub struct GmailClient {
    http_client: reqwest::Client,
    token_provider: Box<dyn TokenProvider>,
}

impl GmailClient {
    pub fn builder() -> GmailClientBuilder {
        GmailClientBuilder::new()
    }

    pub async fn fetch_messages(
        &self,
        max_results: Option<u32>,
    ) -> Result<Vec<GmailMessage>, String> {
        let access_token = self.token_provider.get_access_token().await?;

        let max = max_results.unwrap_or(10);
        let url = format!("{}/users/me/messages?maxResults={}", GMAIL_API_BASE, max);

        let response = self
            .http_client
            .get(&url)
            .bearer_auth(&access_token)
            .send()
            .await
            .map_err(|e| format!("Failed to fetch messages: {}", e))?;

        let status = response.status();
        let response_text = response
            .text()
            .await
            .map_err(|e| format!("Failed to read response: {}", e))?;

        if !status.is_success() {
            return Err(format!("Gmail API error ({}): {}", status, response_text));
        }

        let list_response: GmailMessageListResponse = serde_json::from_str(&response_text)
            .map_err(|e| format!("Failed to parse message list: {}", e))?;

        Ok(list_response.messages.unwrap_or_default())
    }

    pub async fn fetch_message(&self, message_id: &str) -> Result<GmailFullMessage, String> {
        let access_token = self.token_provider.get_access_token().await?;

        let url = format!("{}/users/me/messages/{}", GMAIL_API_BASE, message_id);

        let response = self
            .http_client
            .get(&url)
            .bearer_auth(&access_token)
            .send()
            .await
            .map_err(|e| format!("Failed to fetch message: {}", e))?;

        let status = response.status();
        let response_text = response
            .text()
            .await
            .map_err(|e| format!("Failed to read response: {}", e))?;

        if !status.is_success() {
            return Err(format!("Gmail API error ({}): {}", status, response_text));
        }

        serde_json::from_str(&response_text).map_err(|e| format!("Failed to parse message: {}", e))
    }

    pub fn parse_email(&self, message: &GmailFullMessage) -> Result<ParsedEmail, String> {
        let mut parsed = ParsedEmail {
            provider_id: message.id.clone(),
            subject: None,
            from_email: None,
            from_name: None,
            received_at: message.internal_date.clone(),
            body_html: None,
            body_text: None,
            snippet: message.snippet.clone(),
        };

        if let Some(ref payload) = message.payload {
            // Extract headers
            if let Some(ref headers) = payload.headers {
                for header in headers {
                    match header.name.as_str() {
                        "Subject" => parsed.subject = Some(header.value.clone()),
                        "From" => {
                            let from = parse_from_header(&header.value);
                            parsed.from_email = from.0;
                            parsed.from_name = from.1;
                        }
                        "Date" => {
                            if parsed.received_at.is_none() {
                                parsed.received_at = Some(header.value.clone());
                            }
                        }
                        _ => {}
                    }
                }
            }

            // Extract body
            extract_body(payload, &mut parsed);
        }

        Ok(parsed)
    }
}

pub struct GmailClientBuilder {
    http_client: Option<reqwest::Client>,
    token_provider: Option<Box<dyn TokenProvider>>,
}

impl GmailClientBuilder {
    pub fn new() -> Self {
        Self {
            http_client: None,
            token_provider: None,
        }
    }

    pub fn http_client(mut self, client: reqwest::Client) -> Self {
        self.http_client = Some(client);
        self
    }

    pub fn token_provider(mut self, provider: Box<dyn TokenProvider>) -> Self {
        self.token_provider = Some(provider);
        self
    }

    pub fn build(self) -> Result<GmailClient, String> {
        let http_client = self.http_client.unwrap_or_else(|| reqwest::Client::new());
        let token_provider = self.token_provider.ok_or("Token provider is required")?;

        Ok(GmailClient {
            http_client,
            token_provider,
        })
    }
}

fn parse_from_header(from: &str) -> (Option<String>, Option<String>) {
    // Parse "Name <email@example.com>" or "email@example.com"
    if let Some(start) = from.find('<') {
        if let Some(end) = from.find('>') {
            let email = from[start + 1..end].trim().to_string();
            let name = from[..start].trim().trim_matches('"').to_string();
            return (Some(email), if name.is_empty() { None } else { Some(name) });
        }
    }

    // If no angle brackets, assume it's just an email
    (Some(from.trim().to_string()), None)
}

fn extract_body(part: &GmailMessagePart, parsed: &mut ParsedEmail) {
    if let Some(ref parts) = part.parts {
        for p in parts {
            extract_body(p, parsed);
        }
    }

    if let Some(ref body) = part.body {
        if let Some(ref data) = body.data {
            use base64::{engine::general_purpose, Engine as _};
            let decoded = general_purpose::STANDARD
                .decode(data.replace("-", "+").replace("_", "/"))
                .ok()
                .and_then(|bytes| String::from_utf8(bytes).ok());

            if let Some(decoded) = decoded {
                match part.mime_type.as_deref() {
                    Some("text/html") => {
                        parsed.body_html = Some(decoded);
                    }
                    Some("text/plain") => {
                        parsed.body_text = Some(decoded);
                    }
                    _ => {}
                }
            }
        }
    }
}
