use crate::types::{FolderInfo, MailError, MailServerConfig};

use mail_parser::MimeHeaders;

use lazy_static::lazy_static;
use std::collections::HashMap;
use tokio::sync::Mutex;

lazy_static! {
    // Global tracker for IMAP IDLE cancellation channels, keyed by email
    static ref IDLE_CANCELS: std::sync::Arc<Mutex<HashMap<String, tokio::sync::mpsc::Sender<()>>>> = std::sync::Arc::new(Mutex::new(HashMap::new()));
}

pub fn init_logger() {
    tracing_subscriber::fmt::init();

    // Initialize Rustls with 'ring' as the default provider to prevent crashes
    // when multiple providers (ring, aws-lc-rs) are present in the dependency graph.
    // This is required for Rustls 0.23+
    let _ = rustls::crypto::ring::default_provider().install_default();
}

pub async fn discover_server(email: String) -> Result<MailServerConfig, MailError> {
    crate::dns::discover_mail_server(&email)
        .await
        .map_err(|e| MailError::Network(e.to_string()))
}

/// A simple test function to verify we can connect and list folders.
pub async fn connect_and_list_folders(
    config: MailServerConfig,
    email: String,
    password: String,
    show_all_folders: bool,
    storage_path: String,
) -> Result<Vec<FolderInfo>, MailError> {
    // This call will create/cache the session in the manager
    let client_arc = crate::session_manager::get_client(config, email, password).await?;

    let mut client = client_arc.lock().await;
    let mut folders = client
        .list_folders(show_all_folders)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    // Add unread counts from storage for all folders
    if let Ok(storage) = crate::storage::Storage::new(&storage_path) {
        for folder in &mut folders {
            if let Ok(count) = storage.get_unread_count(&folder.name) {
                folder.unread_count = count;
            }
        }
    }

    Ok(folders)
}

pub fn get_total_unread_count(storage_path: String) -> i32 {
    if let Ok(storage) = crate::storage::Storage::new(&storage_path) {
        storage.get_total_unread_count().unwrap_or(0)
    } else {
        0
    }
}

pub async fn get_messages(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
) -> Result<Vec<crate::types::MessageHeader>, MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };

    let client_arc = crate::session_manager::get_client(config, email, password).await?;

    let mut client = client_arc.lock().await;
    client
        .select_mailbox(&mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    client
        .fetch_headers("1:*")
        .await
        .map_err(|e| MailError::Network(e.to_string()))
}

pub async fn get_message_body(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    uid: u32,
    storage_path: String,
    _passphrase: Option<String>,
) -> Result<crate::types::MessageBody, MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };
    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;

    let cached_raw = storage
        .get_message_raw(&mailbox, uid)
        .map_err(|e| MailError::Generic(e.to_string()))?;

    let body_result = if let Some(raw_bytes) = cached_raw {
        tracing::debug!("Cache hit for message UID {} in {}", uid, mailbox);
        parse_mime_message(&raw_bytes).await.map(|mut body| {
            body.uid = uid;
            body
        })
    } else {
        let mut client = crate::imap::ImapClient::new(config.clone(), email.clone());
        client
            .connect(&password)
            .await
            .map_err(|e| MailError::Network(e.to_string()))?;

        client
            .select_mailbox(&mailbox)
            .await
            .map_err(|e| MailError::Network(e.to_string()))?;

        let fetch_result = client.fetch_body_raw(uid).await;

        // Always close the dedicated connection, even on error.
        let _ = client.logout().await;

        let raw_bytes = match fetch_result {
            Ok(bytes) => bytes,
            Err(e) => {
                let err_msg = e.to_string();
                if err_msg.contains("not found on server") {
                    let _ = storage.delete_header(&mailbox, uid);
                }
                return Err(MailError::Network(err_msg));
            }
        };

        // Save to cache
        if let Err(e) = storage.save_message_raw(&mailbox, uid, &raw_bytes) {
            tracing::warn!("Failed to save message body to cache: {}", e);
        }

        parse_mime_message(&raw_bytes).await.map(|mut body| {
            body.uid = uid;
            body
        })
    };

    body_result
}

pub async fn set_message_flag(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    uid: u32,
    flag: Option<String>,
    storage_path: String,
) -> Result<(), MailError> {
    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;

    // Optimistically update local DB
    if let Err(e) = storage.update_message_flag(&mailbox, uid, flag.clone()) {
        tracing::warn!("Failed to update flag in local storage: {}", e);
    }

    // Dedicated connection — never blocked by sync_mailbox holding the shared session lock.
    let mut client = crate::imap::ImapClient::new(config, email);
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let result = client
        .set_message_flag(&mailbox, uid, flag)
        .await
        .map_err(|e| MailError::Network(e.to_string()));

    // Always close the dedicated connection, even on error.
    let _ = client.logout().await;

    result
}

pub fn save_contact(path: String, contact: crate::types::Contact) -> Result<(), MailError> {
    let storage =
        crate::storage::Storage::new(&path).map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .save_contact(&contact)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn get_contacts(path: String) -> Result<Vec<crate::types::Contact>, MailError> {
    let storage =
        crate::storage::Storage::new(&path).map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .get_contacts()
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn delete_contact(path: String, email: String) -> Result<(), MailError> {
    let storage =
        crate::storage::Storage::new(&path).map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .delete_contact(&email)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn get_contact_by_email(
    path: String,
    email: String,
) -> Result<Option<crate::types::Contact>, MailError> {
    let storage =
        crate::storage::Storage::new(&path).map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .get_contact_by_email(&email)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub async fn append_to_sent(
    config: &MailServerConfig,
    email: &str,
    password: &str,
    message_bytes: &[u8],
) {
    let mut client = crate::imap::ImapClient::new(config.clone(), email.to_string());
    if let Err(e) = client.connect(password).await {
        tracing::warn!("append_to_sent: IMAP connect failed: {}", e);
        return;
    }
    let folders = client.list_folders(false).await.unwrap_or_default();
    let sent_folder = folders
        .iter()
        .find(|f| f.folder_type == crate::types::FolderType::Sent)
        .map(|f| f.name.clone())
        .unwrap_or_else(|| "Sent".to_string());
    if let Err(e) = client
        .append_message(&sent_folder, message_bytes, Some(r"\Seen".to_string()))
        .await
    {
        tracing::warn!("append_to_sent: append to {} failed: {}", sent_folder, e);
    }
    let _ = client.logout().await;
}

#[allow(clippy::too_many_arguments)]
pub async fn send_email(
    config: MailServerConfig,
    email: String,
    password: String,
    to: String,
    subject: String,
    body: String,
    attachments: Vec<crate::types::Attachment>,
    encrypt: bool,
    _signer_key: Option<String>,
    _signer_password: Option<String>,
    _storage_path: String,
) -> Result<(), MailError> {
    if encrypt {
        return Err(MailError::Generic("PGP encryption not supported in mailcore. Use mailmore.".to_string()));
    }

    let client = crate::smtp::SmtpClient::new(config.clone(), email.clone());
    let message_bytes = client
        .send_mail(&password, &to, &subject, &body, attachments)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    append_to_sent(&config, &email, &password, &message_bytes).await;
    Ok(())
}

pub async fn prefetch_bodies(
    config: crate::types::MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    uids: Vec<u32>,
    storage_path: String,
) -> Result<(), MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;

    // Filter to only uncached UIDs before opening a connection
    let uncached: Vec<u32> = uids
        .into_iter()
        .filter(|&uid| !matches!(storage.get_message_raw(&mailbox, uid), Ok(Some(_))))
        .collect();

    if uncached.is_empty() {
        return Ok(());
    }

    // Use a DEDICATED connection so prefetch never blocks user-initiated fetches
    let mut client = crate::imap::ImapClient::new(config, email.clone());
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    client
        .select_mailbox(&mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    for uid in uncached {
        match client.fetch_body_raw(uid).await {
            Ok(raw_bytes) => {
                if let Err(e) = storage.save_message_raw(&mailbox, uid, &raw_bytes) {
                    tracing::warn!("Prefetch: Failed to save body for UID {}: {}", uid, e);
                }
            }
            Err(e) => {
                tracing::warn!("Prefetch: Failed to fetch body for UID {}: {}", uid, e);
            }
        }
    }

    // Always close the dedicated connection when prefetch is done.
    let _ = client.logout().await;

    Ok(())
}

pub async fn parse_mime_message(bytes: &[u8]) -> Result<crate::types::MessageBody, MailError> {
    let bytes_owned = crate::imap::client::preprocess_header_bytes(bytes);
    tokio::task::spawn_blocking(move || {
        let parsed = mail_parser::MessageParser::default()
            .parse(&bytes_owned)
            .ok_or_else(|| MailError::Generic("Failed to parse MIME message".to_string()))?;

        let mut attachments = Vec::new();
        for part in parsed.attachments() {
            let content_type = part
                .content_type()
                .map(|ct| {
                    let ctype = ct.ctype();
                    let subtype = ct.subtype();
                    format!("{}/{}", ctype, subtype.unwrap_or(""))
                })
                .unwrap_or_else(|| "application/octet-stream".to_string());

            attachments.push(crate::types::Attachment {
                name: part.attachment_name().unwrap_or("unnamed").to_string(),
                content_type,
                size: part.contents().len(),
                content: part.contents().to_vec(),
            });
        }

        // RFC 3156 PGP/MIME: mail_parser's attachments() does not expose the inner
        // parts of a multipart/encrypted envelope. Walk all parsed parts directly to
        // find the application/octet-stream ciphertext that attachments() misses.
        let mut is_pgp_mime_envelope = false;
        if let Some(ct) = parsed.content_type() {
            if ct.ctype() == "multipart" && ct.subtype().unwrap_or("") == "encrypted" {
                is_pgp_mime_envelope = true;
                for part in &parsed.parts {
                    if let Some(pct) = part.content_type() {
                        if pct.ctype() == "application"
                            && pct.subtype().unwrap_or("") == "octet-stream"
                        {
                            let name = part
                                .attachment_name()
                                .unwrap_or("encrypted.asc")
                                .to_string();
                            let content = part.contents().to_vec();
                            if !attachments
                                .iter()
                                .any(|a: &crate::types::Attachment| a.content == content)
                            {
                                attachments.push(crate::types::Attachment {
                                    name,
                                    content_type: "application/octet-stream".to_string(),
                                    size: content.len(),
                                    content,
                                });
                            }
                        }
                    }
                }
            }
        }

        let (pgp_status, signature_fingerprint) = detect_pgp_status(&parsed);

        let from_addr = parsed.from().and_then(|f| {
            f.iter()
                .next()
                .map(|a| a.address().unwrap_or("").to_string())
        });

        // For multipart/encrypted messages, body_text(0) returns raw MIME bytes
        // (boundary markers, Content-Type headers, the armored PGP block) rather
        // than meaningful body text. Suppress it so the caller never shows protocol
        // noise to the user.
        let (text_plain, text_html) = if is_pgp_mime_envelope {
            (None, None)
        } else {
            (
                parsed.body_text(0).map(|s| s.to_string()),
                parsed.body_html(0).map(|s| s.to_string()),
            )
        };

        Ok(crate::types::MessageBody {
            uid: 0, // No UID for raw bytes
            from: from_addr,
            text_plain,
            text_html,
            attachments,
            pgp_status,
            signature_fingerprint,
        })
    })
    .await
    .unwrap_or_else(|e| Err(MailError::Generic(format!("Task spawn error: {}", e))))
}

fn detect_pgp_status(parsed: &mail_parser::Message) -> (crate::types::PgpStatus, Option<String>) {
    let mut pgp_status = crate::types::PgpStatus::None;
    let signature_fingerprint = None;

    // Basic PGP detection from headers
    if let Some(ct) = parsed.content_type() {
        let ctype = ct.ctype();
        let subtype = ct.subtype().unwrap_or("");
        if ctype == "multipart" && subtype == "signed" {
            pgp_status = crate::types::PgpStatus::Signed;
        } else if ctype == "multipart" && subtype == "encrypted" {
            pgp_status = crate::types::PgpStatus::Encrypted;
        }
    }

    // Check text/plain body for inline PGP MESSAGE block
    if pgp_status == crate::types::PgpStatus::None {
        if let Some(text) = parsed.body_text(0) {
            if text.contains("-----BEGIN PGP MESSAGE-----") {
                pgp_status = crate::types::PgpStatus::Encrypted;
            }
        }
    }

    // Try to find if signed or if we find a signature part
    for part in &parsed.parts {
        // Detect unnamed application/octet-stream containing PGP MESSAGE
        if pgp_status == crate::types::PgpStatus::None {
            let is_octet = part
                .content_type()
                .map(|ct| ct.ctype() == "application" && ct.subtype() == Some("octet-stream"))
                .unwrap_or(false);
            // Also match parts that have no content-type at all (servers strip it)
            let has_no_ct = part.content_type().is_none();
            if is_octet || has_no_ct {
                let contents = part.contents();
                if contents.starts_with(b"-----BEGIN PGP MESSAGE-----") {
                    pgp_status = crate::types::PgpStatus::Encrypted;
                }
            }
        }

        if let Some(ct) = part.content_type() {
            if ct.subtype() == Some("pgp-signature") {
                if pgp_status == crate::types::PgpStatus::None {
                    pgp_status = crate::types::PgpStatus::Signed;
                }
            }
        }
    }

    (pgp_status, signature_fingerprint)
}

pub async fn sync_mailbox(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    storage_path: String,
    cache_days: i64,
) -> Result<Vec<crate::types::MessageHeader>, MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };
    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;

    // Step 1: Run sync on the shared session.
    let sync_result = {
        let client_arc =
            crate::session_manager::get_client(config.clone(), email.clone(), password.clone())
                .await?;
        let mut client = client_arc.lock().await;
        client
            .sync_mailbox(&mailbox, &storage, cache_days)
            .await
            .map_err(|e| MailError::Network(e.to_string()))?
    };

    // Step 2: FLAG refresh on a dedicated connection.
    let uids = sync_result.uids_for_flag_refresh;
    if !uids.is_empty() {
        let mut dedicated = crate::imap::ImapClient::new(config, email);
        match dedicated.connect(&password).await {
            Ok(()) => {
                if let Err(e) = dedicated.refresh_flags(&mailbox, &uids, &storage).await {
                    tracing::warn!("FLAG refresh via dedicated connection failed: {}", e);
                }
                let _ = dedicated.logout().await;
            }
            Err(e) => {
                tracing::warn!(
                    "FLAG refresh: dedicated connection failed to connect: {}",
                    e
                );
            }
        }
    }

    Ok(sync_result.new_headers)
}

pub async fn listen_for_updates(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
) -> Result<tokio::sync::mpsc::Receiver<bool>, MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };

    let mut client = crate::imap::ImapClient::new(config.clone(), email.clone());
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    client
        .select_mailbox(&mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let password_clone = password.clone();
    let mailbox_clone = mailbox.clone();
    let email_clone = email.clone();

    let (tx, rx) = tokio::sync::mpsc::channel::<bool>(32);

    let (cancel_tx, mut cancel_rx) = tokio::sync::mpsc::channel::<()>(1);
    {
        let mut cancels = IDLE_CANCELS.lock().await;
        if let Some(old_tx) = cancels.remove(&email_clone) {
            let _ = old_tx.send(()).await;
        }
        cancels.insert(email_clone.clone(), cancel_tx);
    }

    tokio::task::spawn(async move {
        loop {
            tokio::select! {
                _ = cancel_rx.recv() => {
                    tracing::debug!("IDLE loop canceled for {}", email_clone);
                    break;
                }
                res = client.wait_for_update(600) => {
                    match res {
                        Ok(true) => {
                            tracing::debug!("IDLE: New data detected for {}", email_clone);
                            if tx.send(true).await.is_err() {
                                break;
                            }
                            tokio::time::sleep(std::time::Duration::from_secs(5)).await;
                        }
                        Ok(false) => {
                            tracing::debug!("IDLE: Timeout for {}, renewing...", email_clone);
                        }
                        Err(e) => {
                            tracing::error!("IDLE error for {}: {}", email_clone, e);
                            tokio::time::sleep(std::time::Duration::from_secs(5)).await;
                            if let Err(re_err) = client.reconnect(&password_clone).await {
                                tracing::error!("IDLE reconnect failed: {}", re_err);
                                break;
                            }
                            let _ = client.select_mailbox(&mailbox_clone).await;
                        }
                    }
                }
            }
        }
    });

    Ok(rx)
}

pub fn stop_idle_listener(email: String) {
    let mut cancels = IDLE_CANCELS.blocking_lock();
    if let Some(tx) = cancels.remove(&email) {
        tracing::debug!("Canceling existing IDLE listener for {}", email);
        let _ = tx.try_send(());
    }
}

pub async fn check_connection(email: String) -> bool {
    crate::session_manager::is_connected(&email).await
}

pub fn get_cached_messages(
    storage_path: String,
    mailbox: String,
) -> Result<Vec<crate::types::MessageHeader>, MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };
    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .get_headers(&mailbox)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn save_draft(path: String, draft: crate::types::Draft) -> Result<i64, MailError> {
    let storage =
        crate::storage::Storage::new(&path).map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .save_draft(&draft)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn get_drafts(path: String) -> Result<Vec<crate::types::Draft>, MailError> {
    let storage =
        crate::storage::Storage::new(&path).map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .get_drafts()
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn delete_draft(path: String, id: i64) -> Result<(), MailError> {
    let storage =
        crate::storage::Storage::new(&path).map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .delete_draft(id)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub async fn sync_drafts(
    config: MailServerConfig,
    email: String,
    password: String,
    storage_path: String,
) -> Result<(), MailError> {
    let mut client = crate::imap::ImapClient::new(config, email.clone());
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;

    let folders = client
        .list_folders(false)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    let drafts_folder = folders
        .iter()
        .find(|f| f.folder_type == crate::types::FolderType::Drafts)
        .map(|f| f.name.clone())
        .unwrap_or_else(|| "Drafts".to_string());

    client
        .select_mailbox(&drafts_folder)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    let server_headers = client.fetch_headers("1:*").await.unwrap_or_default();
    let server_uids: std::collections::HashSet<u32> =
        server_headers.iter().map(|h| h.uid).collect();

    let local_drafts = storage
        .get_drafts()
        .map_err(|e| MailError::Generic(e.to_string()))?;

    for mut draft in local_drafts {
        let mut needs_upload = false;

        if let Some(uid) = draft.server_uid {
            if !server_uids.contains(&uid) {
                draft.server_uid = None;
                draft.last_synced_at = 0;
                needs_upload = true;
            } else if draft.last_updated > draft.last_synced_at {
                let _ = client.delete_message(&drafts_folder, uid).await;
                draft.server_uid = None;
                needs_upload = true;
            }
        } else {
            needs_upload = true;
        }

        if needs_upload {
            let mut builder = mail_builder::MessageBuilder::new();
            builder = builder
                .from(email.as_str())
                .to(draft.to.as_str())
                .subject(draft.subject.as_str())
                .text_body(draft.body.as_str());

            for att in &draft.attachments {
                builder = builder.attachment(
                    att.content_type.as_str(),
                    att.name.as_str(),
                    att.content.clone(),
                );
            }

            let message_bytes = builder
                .write_to_vec()
                .map_err(|e| MailError::Generic(e.to_string()))?;

            if let Ok(_) = client
                .append_message(&drafts_folder, &message_bytes, Some(r"\Draft".to_string()))
                .await
            {
                draft.last_synced_at = draft.last_updated;
                let _ = storage.save_draft(&draft);
            }
        }
    }

    let updated_local_drafts = storage
        .get_drafts()
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let local_uids: std::collections::HashSet<u32> = updated_local_drafts
        .iter()
        .filter_map(|d| d.server_uid)
        .collect();

    for header in server_headers {
        if !local_uids.contains(&header.uid) {
            if let Ok(body) = client.fetch_body(header.uid).await {
                let new_draft = crate::types::Draft {
                    id: None,
                    to: header.to.join(", "),
                    subject: header.subject.clone(),
                    body: body.text_plain.unwrap_or_default(),
                    attachments: vec![],
                    last_updated: header.date,
                    last_synced_at: header.date,
                    server_uid: Some(header.uid),
                };
                let _ = storage.save_draft(&new_draft);
            }
        }
    }

    let _ = client.logout().await;
    Ok(())
}

pub async fn move_message(
    config: MailServerConfig,
    email: String,
    password: String,
    from_mailbox: String,
    to_mailbox: String,
    uid: u32,
    storage_path: String,
) -> Result<(), MailError> {
    let from_mailbox = if from_mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        from_mailbox
    };
    let to_mailbox = if to_mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        to_mailbox
    };
    let mut client = crate::imap::ImapClient::new(config, email);
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let move_result = client
        .move_message(uid, &from_mailbox, &to_mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()));

    let _ = client.logout().await;

    move_result?;

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let _ = storage.delete_header(&from_mailbox, uid);

    Ok(())
}

pub async fn move_messages(
    config: MailServerConfig,
    email: String,
    password: String,
    from_mailbox: String,
    to_mailbox: String,
    uids: Vec<u32>,
    storage_path: String,
) -> Result<(), MailError> {
    let from_mailbox = if from_mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        from_mailbox
    };
    let to_mailbox = if to_mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        to_mailbox
    };
    if uids.is_empty() {
        return Ok(());
    }

    let mut client = crate::imap::ImapClient::new(config, email);
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let move_result = client
        .move_messages(&uids, &from_mailbox, &to_mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()));

    let _ = client.logout().await;

    move_result?;

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let _ = storage.delete_headers(&from_mailbox, &uids);

    Ok(())
}

pub async fn mark_as_read(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    uid: u32,
    storage_path: String,
) -> Result<(), MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };

    let mut client = crate::imap::ImapClient::new(config, email.clone());
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let mark_result = client
        .mark_as_read(&mailbox, uid)
        .await
        .map_err(|e| MailError::Network(e.to_string()));

    let _ = client.logout().await;

    mark_result?;

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let _ = storage.update_read_status(&mailbox, uid, true);

    Ok(())
}

pub async fn mark_as_unread(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    uid: u32,
    storage_path: String,
) -> Result<(), MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };

    let mut client = crate::imap::ImapClient::new(config, email.clone());
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let mark_result = client
        .mark_as_unread(&mailbox, uid)
        .await
        .map_err(|e| MailError::Network(e.to_string()));

    let _ = client.logout().await;

    mark_result?;

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let _ = storage.update_read_status(&mailbox, uid, false);

    Ok(())
}

pub async fn mark_all_as_read(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    storage_path: String,
) -> Result<(), MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };

    let mut client = crate::imap::ImapClient::new(config, email.clone());
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let mark_result = client
        .mark_all_as_read(&mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()));

    let _ = client.logout().await;

    mark_result?;

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let _ = storage.mark_all_as_read(&mailbox);

    Ok(())
}

pub async fn delete_all_messages(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    storage_path: String,
) -> Result<(), MailError> {
    let mailbox = if mailbox.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        mailbox
    };

    let mut client = crate::imap::ImapClient::new(config, email.clone());
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let result = client
        .delete_all_messages(&mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()));

    let _ = client.logout().await;

    result?;

    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let _ = storage.wipe_folder(&mailbox);

    Ok(())
}

pub async fn create_folder(
    config: MailServerConfig,
    email: String,
    password: String,
    folder_name: String,
) -> Result<(), MailError> {
    let folder_name = if folder_name.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        folder_name
    };
    let mut client = crate::imap::ImapClient::new(config, email);
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    let result = client.create_folder(&folder_name).await.map_err(|e| {
        MailError::Generic(e.to_string())
    });
    let _ = client.logout().await;
    result
}

pub async fn delete_folder(
    config: MailServerConfig,
    email: String,
    password: String,
    folder_name: String,
    storage_path: String,
) -> Result<(), MailError> {
    let folder_name = if folder_name.to_uppercase() == "INBOX" {
        "INBOX".to_string()
    } else {
        folder_name
    };
    let mut client = crate::imap::ImapClient::new(config, email);
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    let result = client.delete_folder(&folder_name).await.map_err(|e| {
        MailError::Generic(e.to_string())
    });
    let _ = client.logout().await;
    result?;
    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    storage
        .wipe_folder(&folder_name)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    Ok(())
}

pub fn save_password(account: String, service: String, password: String) -> Result<(), MailError> {
    crate::secrets::SecretManager::set_password(&account, &service, &password)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn get_password(account: String, service: String) -> Result<Option<String>, MailError> {
    crate::secrets::SecretManager::get_password(&account, &service)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub fn delete_credential(account: String, service: String) -> Result<(), MailError> {
    crate::secrets::SecretManager::delete_password(&account, &service)
        .map_err(|e| MailError::Generic(e.to_string()))
}

pub async fn load_older_messages(
    config: MailServerConfig,
    email: String,
    password: String,
    mailbox: String,
    oldest_uid: u32,
    batch_size: u32,
    storage_path: String,
) -> Result<Vec<crate::types::MessageHeader>, MailError> {
    if oldest_uid == 0 {
        return Ok(vec![]);
    }
    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;

    let local_uids = storage.get_uids(&mailbox).unwrap_or_default();

    let mut client = crate::imap::ImapClient::new(config, email);
    client
        .connect(&password)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    client
        .select_mailbox(&mailbox)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let older_uids: Vec<u32> = client
        .search_uids_below(oldest_uid)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;
    let mut to_fetch: Vec<u32> = older_uids
        .into_iter()
        .filter(|uid| !local_uids.contains(uid))
        .take(batch_size as usize)
        .collect();

    if to_fetch.is_empty() {
        let _ = client.logout().await;
        return Ok(vec![]);
    }

    to_fetch.sort_unstable();
    let range = to_fetch
        .iter()
        .map(|u| u.to_string())
        .collect::<Vec<_>>()
        .join(",");
    let headers = client
        .fetch_headers(&range)
        .await
        .map_err(|e| MailError::Network(e.to_string()))?;

    let mut result = Vec::new();
    for header in headers {
        let _ = storage.save_header(&mailbox, &header);
        result.push(header);
    }

    let _ = client.logout().await;
    Ok(result)
}

pub fn get_folder_stats(
    email: String,
    mailbox: String,
    storage_path: String,
) -> Result<crate::types::FolderStats, MailError> {
    let storage = crate::storage::Storage::new(&storage_path)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    let (unread, server_total) = storage
        .get_folder_stats(&email, &mailbox)
        .map_err(|e| MailError::Generic(e.to_string()))?;
    Ok(crate::types::FolderStats {
        unread,
        server_total,
    })
}
