//! IMAP client functionality for mail2couch
//! 
//! This module provides IMAP client operations for connecting to mail servers,
//! listing mailboxes, and retrieving messages.

use crate::config::{MailSource, MessageFilter};
use crate::schemas::{MailDocument, AttachmentStub};
use anyhow::{anyhow, Result};
use async_imap::types::Fetch;
use async_imap::{Client, Session};
use async_native_tls::{TlsConnector, TlsStream};
use async_std::io::{Read, Write};
use async_std::net::TcpStream;
use async_std::stream::StreamExt;
use async_std::task::{Context, Poll};
use chrono::{DateTime, Utc};
use mail_parser::{Message, MimeHeaders};
use std::collections::HashMap;
use std::pin::Pin;
use std::time::Duration;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ImapError {
    #[error("Connection failed: {0}")]
    Connection(String),
    #[error("Authentication failed: {0}")]
    Authentication(String),
    #[error("IMAP operation failed: {0}")]
    Operation(String),
    #[error("Message parsing failed: {0}")]
    Parsing(String),
}

/// Wrapper for both TLS and plain TCP streams
pub enum ImapStream {
    Plain(TcpStream),
    Tls(TlsStream<TcpStream>),
}

impl Read for ImapStream {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<std::io::Result<usize>> {
        match self.get_mut() {
            ImapStream::Plain(stream) => Pin::new(stream).poll_read(cx, buf),
            ImapStream::Tls(stream) => Pin::new(stream).poll_read(cx, buf),
        }
    }
}

impl Write for ImapStream {
    fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<std::io::Result<usize>> {
        match self.get_mut() {
            ImapStream::Plain(stream) => Pin::new(stream).poll_write(cx, buf),
            ImapStream::Tls(stream) => Pin::new(stream).poll_write(cx, buf),
        }
    }

    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
        match self.get_mut() {
            ImapStream::Plain(stream) => Pin::new(stream).poll_flush(cx),
            ImapStream::Tls(stream) => Pin::new(stream).poll_flush(cx),
        }
    }

    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
        match self.get_mut() {
            ImapStream::Plain(stream) => Pin::new(stream).poll_close(cx),
            ImapStream::Tls(stream) => Pin::new(stream).poll_close(cx),
        }
    }
}

impl std::fmt::Debug for ImapStream {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ImapStream::Plain(_) => write!(f, "ImapStream::Plain(_)"),
            ImapStream::Tls(_) => write!(f, "ImapStream::Tls(_)"),
        }
    }
}

pub type ImapSession = Session<ImapStream>;

/// IMAP client for mail operations
pub struct ImapClient {
    session: Option<ImapSession>,
    source: MailSource,
}

/// Represents a mailbox on the IMAP server
#[derive(Debug, Clone)]
pub struct MailboxInfo {
    pub name: String,
    pub exists: u32,
    pub recent: u32,
    pub uid_validity: Option<u32>,
    pub uid_next: Option<u32>,
}

impl ImapClient {
    /// Create a new IMAP client and connect to the server with retry logic
    pub async fn connect(source: MailSource) -> Result<Self> {
        let mut client = ImapClient {
            session: None,
            source,
        };
        
        client.establish_connection_with_retry().await?;
        Ok(client)
    }

    /// Establish connection with automatic retry logic
    async fn establish_connection_with_retry(&mut self) -> Result<()> {
        const MAX_RETRIES: u32 = 3;
        const RETRY_DELAY_MS: u64 = 1000;

        let mut last_error = None;

        for attempt in 1..=MAX_RETRIES {
            match self.establish_connection().await {
                Ok(()) => {
                    if attempt > 1 {
                        log::info!("✅ IMAP connection successful on attempt {}", attempt);
                    }
                    return Ok(());
                }
                Err(e) => {
                    last_error = Some(e);
                    if attempt < MAX_RETRIES {
                        log::warn!(
                            "🔄 IMAP connection attempt {} failed, retrying in {}ms: {}",
                            attempt,
                            RETRY_DELAY_MS,
                            last_error.as_ref().unwrap()
                        );
                        async_std::task::sleep(Duration::from_millis(RETRY_DELAY_MS)).await;
                    }
                }
            }
        }

        Err(anyhow!(
            "Failed to establish IMAP connection after {} attempts. Last error: {}",
            MAX_RETRIES,
            last_error.unwrap()
        ))
    }


    /// Establish connection to IMAP server
    async fn establish_connection(&mut self) -> Result<()> {
        // Connect to the IMAP server
        let addr = format!("{}:{}", self.source.host, self.source.port);
        let tcp_stream = TcpStream::connect(&addr).await
            .map_err(|e| ImapError::Connection(format!("Failed to connect to {}: {}", addr, e)))?;

        // Determine if we should use TLS based on port
        let imap_stream = if self.should_use_tls() {
            // Use TLS for secure connection (typically port 993)
            let tls_connector = TlsConnector::new();
            let tls_stream = tls_connector.connect(&self.source.host, tcp_stream).await
                .map_err(|e| ImapError::Connection(format!("TLS connection failed: {}", e)))?;
            ImapStream::Tls(tls_stream)
        } else {
            // Use plain connection (typically port 143 or test environments)
            ImapStream::Plain(tcp_stream)
        };

        // Log connection type for debugging
        let connection_type = if self.should_use_tls() { "TLS" } else { "Plain" };
        log::debug!(
            "Connecting to {}:{} using {} connection",
            self.source.host,
            self.source.port,
            connection_type
        );

        // Create IMAP client
        let client = Client::new(imap_stream);

        // Perform IMAP login
        let session = client
            .login(&self.source.user, &self.source.password)
            .await
            .map_err(|e| ImapError::Authentication(format!("Login failed: {:?}", e)))?;

        self.session = Some(session);
        Ok(())
    }

    /// Determine if TLS should be used based on port and configuration
    fn should_use_tls(&self) -> bool {
        // Standard IMAPS port (993) uses TLS
        // Port 143 is typically plain IMAP
        // Port 3143 is used in test environments (plain)
        match self.source.port {
            993 => true,   // Standard IMAPS port
            143 => false,  // Standard IMAP port
            3143 => false, // Test environment port
            _ => {
                // For other ports, default to TLS for security
                // but log a warning
                log::warn!(
                    "Unknown IMAP port {}, defaulting to TLS. Use port 143 for plain IMAP or 993 for TLS",
                    self.source.port
                );
                true
            }
        }
    }

    /// List all mailboxes
    pub async fn list_mailboxes(&mut self) -> Result<Vec<String>> {
        let session = self.session.as_mut()
            .ok_or_else(|| anyhow!("Not connected to IMAP server"))?;

        let mut mailboxes = session.list(Some(""), Some("*")).await
            .map_err(|e| ImapError::Operation(format!("Failed to list mailboxes: {:?}", e)))?;

        let mut mailbox_names = Vec::new();
        while let Some(mailbox_result) = mailboxes.next().await {
            match mailbox_result {
                Ok(mailbox) => mailbox_names.push(mailbox.name().to_string()),
                Err(e) => return Err(ImapError::Operation(format!("Error processing mailbox: {:?}", e)).into()),
            }
        }

        Ok(mailbox_names)
    }

    /// List mailboxes using IMAP LIST with server-side pattern filtering
    pub async fn list_filtered_mailboxes(&mut self, filter: &crate::config::FolderFilter) -> Result<Vec<String>> {
        let session = self.session.as_mut()
            .ok_or_else(|| anyhow!("Not connected to IMAP server"))?;

        let mut all_mailboxes = Vec::new();
        let mut seen = std::collections::HashSet::new();

        // If no include patterns, get all mailboxes
        if filter.include.is_empty() {
            return self.list_mailboxes().await;
        }

        // Use IMAP LIST with each include pattern for server-side filtering
        for pattern in &filter.include {
            log::debug!("Listing mailboxes with pattern: {}", pattern);
            
            let mut mailboxes = session.list(Some(""), Some(pattern)).await
                .map_err(|e| {
                    log::warn!("Failed to list mailboxes with pattern '{}': {:?}", pattern, e);
                    ImapError::Operation(format!("Failed to list mailboxes with pattern '{}': {:?}", pattern, e))
                })?;

            while let Some(mailbox_result) = mailboxes.next().await {
                match mailbox_result {
                    Ok(mailbox) => {
                        let name = mailbox.name().to_string();
                        if seen.insert(name.clone()) {
                            all_mailboxes.push(name);
                        }
                    }
                    Err(e) => {
                        log::warn!("Error processing mailbox with pattern '{}': {:?}", pattern, e);
                        continue;
                    }
                }
            }
        }

        // Apply exclude filters client-side (IMAP LIST doesn't support exclusion)
        if filter.exclude.is_empty() {
            return Ok(all_mailboxes);
        }

        let filtered_mailboxes: Vec<String> = all_mailboxes
            .into_iter()
            .filter(|mailbox| {
                !filter.exclude.iter().any(|exclude_pattern| {
                    self.matches_imap_pattern(exclude_pattern, mailbox)
                })
            })
            .collect();

        Ok(filtered_mailboxes)
    }

    /// Match IMAP-style patterns (simple * wildcard matching for exclude filters)
    fn matches_imap_pattern(&self, pattern: &str, name: &str) -> bool {
        // Handle exact match
        if pattern == name {
            return true;
        }

        // Handle simple prefix wildcard: "Work*" should match "Work/Projects"
        if pattern.ends_with('*') && !pattern[..pattern.len()-1].contains('*') {
            let prefix = &pattern[..pattern.len()-1];
            return name.starts_with(prefix);
        }

        // Handle simple suffix wildcard: "*Temp" should match "Work/Temp"
        if pattern.starts_with('*') && !pattern[1..].contains('*') {
            let suffix = &pattern[1..];
            return name.ends_with(suffix);
        }

        // Handle contains wildcard: "*Temp*" should match "Work/Temp/Archive"
        if pattern.starts_with('*') && pattern.ends_with('*') {
            let middle = &pattern[1..pattern.len()-1];
            return name.contains(middle);
        }

        // For other patterns, fall back to basic string comparison
        false
    }

    /// Select a mailbox
    pub async fn select_mailbox(&mut self, mailbox: &str) -> Result<MailboxInfo> {
        let session = self.session.as_mut()
            .ok_or_else(|| anyhow!("Not connected to IMAP server"))?;

        let mailbox_data = session.select(mailbox).await
            .map_err(|e| ImapError::Operation(format!("Failed to select mailbox {}: {:?}", mailbox, e)))?;

        Ok(MailboxInfo {
            name: mailbox.to_string(),
            exists: mailbox_data.exists,
            recent: mailbox_data.recent,
            uid_validity: mailbox_data.uid_validity,
            uid_next: mailbox_data.uid_next,
        })
    }

    /// Search for messages using IMAP SEARCH command with retry logic
    /// Returns UIDs of matching messages
    pub async fn search_messages(&mut self, since_date: Option<&DateTime<Utc>>) -> Result<Vec<u32>> {
        const MAX_RETRIES: u32 = 3;
        const RETRY_DELAY_MS: u64 = 500;
        
        let mut last_error = None;

        for attempt in 1..=MAX_RETRIES {
            let result = self.search_messages_internal(since_date).await;
            
            match result {
                Ok(uids) => {
                    if attempt > 1 {
                        log::debug!("✅ IMAP search successful on attempt {}", attempt);
                    }
                    return Ok(uids);
                }
                Err(e) => {
                    last_error = Some(e);
                    if attempt < MAX_RETRIES {
                        log::warn!(
                            "🔄 IMAP search attempt {} failed, retrying in {}ms: {}",
                            attempt,
                            RETRY_DELAY_MS,
                            last_error.as_ref().unwrap()
                        );
                        async_std::task::sleep(Duration::from_millis(RETRY_DELAY_MS)).await;
                    }
                }
            }
        }

        Err(anyhow!(
            "IMAP search failed after {} attempts. Last error: {}",
            MAX_RETRIES,
            last_error.unwrap()
        ))
    }

    /// Internal search implementation without retry logic
    async fn search_messages_internal(&mut self, since_date: Option<&DateTime<Utc>>) -> Result<Vec<u32>> {
        let session = self.session.as_mut()
            .ok_or_else(|| anyhow!("Not connected to IMAP server"))?;

        let search_query = if let Some(since) = since_date {
            // Format date as required by IMAP (DD-MMM-YYYY)
            // IMAP months are 3-letter abbreviations in English
            let formatted_date = since.format("%d-%b-%Y").to_string();
            log::debug!("Searching for messages since: {}", formatted_date);
            format!("SINCE {}", formatted_date)
        } else {
            log::debug!("Searching for all messages");
            "ALL".to_string()
        };

        log::debug!("IMAP search query: {}", search_query);
        
        let uids = session.uid_search(&search_query).await
            .map_err(|e| ImapError::Operation(format!("Search failed with query '{}': {:?}", search_query, e)))?;

        let uid_vec: Vec<u32> = uids.into_iter().collect();
        log::debug!("Found {} messages matching search criteria", uid_vec.len());
        
        Ok(uid_vec)
    }

    /// Search for messages with advanced criteria
    /// Supports multiple search parameters for more complex queries
    pub async fn search_messages_advanced(
        &mut self,
        since_date: Option<&DateTime<Utc>>,
        before_date: Option<&DateTime<Utc>>,
        subject_keywords: Option<&[String]>,
        from_keywords: Option<&[String]>,
    ) -> Result<Vec<u32>> {
        let session = self.session.as_mut()
            .ok_or_else(|| anyhow!("Not connected to IMAP server"))?;

        let mut search_parts = Vec::new();

        // Add date filters
        if let Some(since) = since_date {
            let formatted_date = since.format("%d-%b-%Y").to_string();
            search_parts.push(format!("SINCE {}", formatted_date));
        }

        if let Some(before) = before_date {
            let formatted_date = before.format("%d-%b-%Y").to_string();
            search_parts.push(format!("BEFORE {}", formatted_date));
        }

        // Add subject keyword filters
        if let Some(keywords) = subject_keywords {
            for keyword in keywords {
                search_parts.push(format!("SUBJECT \"{}\"", keyword.replace("\"", "\\\"")));
            }
        }

        // Add from keyword filters
        if let Some(keywords) = from_keywords {
            for keyword in keywords {
                search_parts.push(format!("FROM \"{}\"", keyword.replace("\"", "\\\"")));
            }
        }

        // Build the final search query
        let search_query = if search_parts.is_empty() {
            "ALL".to_string()
        } else {
            search_parts.join(" ")
        };

        log::debug!("Advanced IMAP search query: {}", search_query);
        
        let uids = session.uid_search(&search_query).await
            .map_err(|e| ImapError::Operation(format!("Advanced search failed with query '{}': {:?}", search_query, e)))?;

        let uid_vec: Vec<u32> = uids.into_iter().collect();
        log::debug!("Found {} messages matching advanced search criteria", uid_vec.len());
        
        Ok(uid_vec)
    }

    /// Fetch message by UID with attachment data
    pub async fn fetch_message(&mut self, uid: u32, mailbox: &str) -> Result<(MailDocument, Vec<(String, String, Vec<u8>)>)> {
        let session = self.session.as_mut()
            .ok_or_else(|| anyhow!("Not connected to IMAP server"))?;

        // Fetch message headers and body
        let mut messages = session.uid_fetch(format!("{}", uid), "RFC822").await
            .map_err(|e| ImapError::Operation(format!("Failed to fetch message {}: {:?}", uid, e)))?;

        // Collect the first message
        if let Some(message_result) = messages.next().await {
            match message_result {
                Ok(message) => {
                    // Drop the messages stream to release the session borrow
                    drop(messages);
                    self.parse_message(&message, uid, mailbox).await
                }
                Err(e) => Err(ImapError::Operation(format!("Failed to process message {}: {:?}", uid, e)).into()),
            }
        } else {
            Err(anyhow!("Message {} not found", uid))
        }
    }

    /// Fetch multiple messages by UIDs with attachment data
    pub async fn fetch_messages(&mut self, uids: &[u32], max_count: Option<u32>, mailbox: &str) -> Result<Vec<(MailDocument, Vec<(String, String, Vec<u8>)>)>> {
        if uids.is_empty() {
            return Ok(Vec::new());
        }

        let session = self.session.as_mut()
            .ok_or_else(|| anyhow!("Not connected to IMAP server"))?;

        // Limit the number of messages if specified
        let uids_to_fetch = if let Some(max) = max_count {
            if uids.len() > max as usize {
                &uids[..max as usize]
            } else {
                uids
            }
        } else {
            uids
        };

        // Create UID sequence
        let uid_sequence = uids_to_fetch.iter()
            .map(|uid| uid.to_string())
            .collect::<Vec<_>>()
            .join(",");

        // Fetch messages
        let mut messages = session.uid_fetch(&uid_sequence, "RFC822").await
            .map_err(|e| ImapError::Operation(format!("Failed to fetch messages: {:?}", e)))?;

        // Collect all messages first to avoid borrowing issues
        let mut fetched_messages = Vec::new();
        while let Some(message_result) = messages.next().await {
            match message_result {
                Ok(message) => fetched_messages.push(message),
                Err(e) => log::warn!("Failed to fetch message: {:?}", e),
            }
        }
        
        // Drop the messages stream to release the session borrow
        drop(messages);

        let mut mail_documents = Vec::new();
        for (i, message) in fetched_messages.iter().enumerate() {
            if let Some(&uid) = uids_to_fetch.get(i) {
                match self.parse_message(message, uid, mailbox).await {
                    Ok((doc, attachments)) => mail_documents.push((doc, attachments)),
                    Err(e) => {
                        log::warn!("Failed to parse message {}: {}", uid, e);
                    }
                }
            }
        }

        Ok(mail_documents)
    }

    /// Parse a raw IMAP message into a MailDocument with attachment data
    async fn parse_message(&self, message: &Fetch, uid: u32, mailbox: &str) -> Result<(MailDocument, Vec<(String, String, Vec<u8>)>)> {
        let body = message.body()
            .ok_or_else(|| ImapError::Parsing("No message body found".to_string()))?;

        // Parse the email using mail-parser library
        let parsed_message = Message::parse(body)
            .ok_or_else(|| ImapError::Parsing("Failed to parse email message".to_string()))?;

        // Extract sender addresses
        let from = self.extract_addresses(&parsed_message, "From");
        
        // Extract recipient addresses
        let to = self.extract_addresses(&parsed_message, "To");

        // Extract subject
        let subject = parsed_message
            .get_subject()
            .unwrap_or("No Subject")
            .to_string();

        // Extract date
        let date = if let Some(date_time) = parsed_message.get_date() {
            DateTime::from_timestamp(date_time.to_timestamp(), 0).unwrap_or_else(|| Utc::now())
        } else {
            Utc::now()
        };

        // Extract body content (prefer text/plain, fallback to text/html)
        let body_content = self.extract_body_content(&parsed_message);

        // Extract headers
        let headers = self.extract_headers(&parsed_message);

        // Extract attachments and their data
        let (has_attachments, attachment_stubs, attachment_data) = self.extract_attachments_with_data(&parsed_message);

        let mut mail_doc = MailDocument::new(
            uid.to_string(),
            mailbox.to_string(),
            from,
            to,
            subject,
            date,
            body_content,
            headers,
            has_attachments,
        );

        // Don't store attachment metadata in the document
        // CouchDB will handle this when we store attachments separately
        // This matches the Go implementation approach

        // Log attachment information
        if !attachment_data.is_empty() {
            log::info!("Found {} attachments for message {}", attachment_data.len(), uid);
        }

        Ok((mail_doc, attachment_data))
    }

    /// Extract email addresses from a parsed message
    fn extract_addresses(&self, message: &Message, header_name: &str) -> Vec<String> {
        if let Some(header) = message.get_header(header_name) {
            // For address headers, use as_text() and parse manually
            // mail-parser doesn't provide a direct address parsing method
            let header_text = header.as_text_ref().unwrap_or("");
            
            // Simple address extraction - split by comma and clean up
            header_text
                .split(',')
                .map(|addr| addr.trim().to_string())
                .filter(|addr| !addr.is_empty() && addr.contains('@'))
                .collect()
        } else {
            Vec::new()
        }
    }

    /// Extract body content from a parsed message (prefer text/plain, fallback to text/html)
    fn extract_body_content(&self, message: &Message) -> String {
        // Try to get text/plain body first (index 0 = first text part)
        if let Some(text_body) = message.get_text_body(0) {
            return text_body.to_string();
        }

        // Fallback to HTML body if no plain text (index 0 = first HTML part)
        if let Some(html_body) = message.get_html_body(0) {
            return html_body.to_string();
        }

        // If neither standard method works, try to extract from parts manually
        for part in &message.parts {
            // Check content type for text parts
            if let Some(content_type) = part.get_content_type() {
                if content_type.c_type.starts_with("text/plain") {
                    if let Some(body) = part.get_text_contents() {
                        return body.to_string();
                    }
                }
            }
        }

        // Second pass for HTML parts if no plain text found
        for part in &message.parts {
            if let Some(content_type) = part.get_content_type() {
                if content_type.c_type.starts_with("text/html") {
                    if let Some(body) = part.get_text_contents() {
                        return body.to_string();
                    }
                }
            }
        }

        // Last resort - try any text content
        for part in &message.parts {
            if let Some(body) = part.get_text_contents() {
                if !body.trim().is_empty() {
                    return body.to_string();
                }
            }
        }

        // Absolutely last resort - empty body
        "No body content found".to_string()
    }

    /// Extract all headers from a parsed message
    fn extract_headers(&self, message: &Message) -> HashMap<String, Vec<String>> {
        let mut headers = HashMap::new();
        
        for header in message.get_headers() {
            let name = header.name().to_lowercase();
            let value = match header.value().as_text_ref() {
                Some(text) => text.to_string(),
                None => format!("{:?}", header.value()), // Fallback for non-text values
            };
            
            headers.entry(name)
                .or_insert_with(Vec::new)
                .push(value);
        }
        
        headers
    }

    /// Extract attachments from a parsed message with binary data
    /// Returns (has_attachments, attachment_stubs, attachment_data)
    fn extract_attachments_with_data(&self, message: &Message) -> (bool, HashMap<String, AttachmentStub>, Vec<(String, String, Vec<u8>)>) {
        let mut attachment_stubs = HashMap::new();
        let mut attachment_data = Vec::new();
        
        // Iterate through all message parts looking for attachments
        for (index, part) in message.parts.iter().enumerate() {
            // Check if this part is an attachment
            if let Some(content_type) = part.get_content_type() {
                let is_attachment = self.is_attachment_part(part, &content_type);
                
                if is_attachment {
                    // Generate a filename for the attachment
                    let filename = self.get_attachment_filename(part, index);
                    
                    // Get the content data (try different methods based on content type)
                    let body_data = if let Some(text_content) = part.get_text_contents() {
                        // Text-based attachments
                        log::debug!("Found text attachment content: {} bytes", text_content.len());
                        text_content.as_bytes().to_vec()
                    } else {
                        // For now, skip attachments without text content
                        // TODO: Implement binary attachment support with proper mail-parser API
                        log::debug!("Skipping non-text attachment for part {} (content-type: {})", index, content_type.c_type);
                        vec![]
                    };
                    
                    let content_type_str = content_type.c_type.to_string();
                    
                    // Only create attachment stub if we have actual data
                    if !body_data.is_empty() {
                        let attachment_stub = AttachmentStub {
                            content_type: content_type_str.clone(),
                            length: Some(body_data.len() as u64),
                            stub: None, // Will be stored as actual attachment data
                        };
                        
                        attachment_stubs.insert(filename.clone(), attachment_stub);
                        attachment_data.push((filename, content_type_str, body_data));
                    }
                }
            }
        }
        
        let has_attachments = !attachment_stubs.is_empty();
        (has_attachments, attachment_stubs, attachment_data)
    }

    /// Extract attachments from a parsed message (deprecated - use extract_attachments_with_data)
    /// Returns (has_attachments, attachment_stubs)
    fn extract_attachments(&self, message: &Message) -> (bool, HashMap<String, AttachmentStub>) {
        let (has_attachments, attachment_stubs, _) = self.extract_attachments_with_data(message);
        (has_attachments, attachment_stubs)
    }
    
    /// Determine if a message part is an attachment
    fn is_attachment_part(&self, part: &mail_parser::MessagePart, content_type: &mail_parser::ContentType) -> bool {
        // Check Content-Disposition header first
        if let Some(disposition) = part.get_content_disposition() {
            return disposition.c_type.to_lowercase() == "attachment";
        }
        
        // If no explicit disposition, check content type
        // Consider non-text types as potential attachments
        let main_type = content_type.c_type.split('/').next().unwrap_or("");
        match main_type {
            "text" => false, // Text parts are usually body content
            "multipart" => false, // Multipart containers are not attachments
            _ => true, // Images, applications, etc. are likely attachments
        }
    }
    
    /// Generate a filename for an attachment
    fn get_attachment_filename(&self, part: &mail_parser::MessagePart, index: usize) -> String {
        // Try to get filename from Content-Disposition
        if let Some(disposition) = part.get_content_disposition() {
            // Find filename in attributes vector
            if let Some(attrs) = &disposition.attributes {
                for (key, value) in attrs {
                    if key.to_lowercase() == "filename" {
                        return value.to_string();
                    }
                }
            }
        }
        
        // Try to get filename from Content-Type
        if let Some(content_type) = part.get_content_type() {
            // Find name in attributes vector
            if let Some(attrs) = &content_type.attributes {
                for (key, value) in attrs {
                    if key.to_lowercase() == "name" {
                        return value.to_string();
                    }
                }
            }
        }
        
        // Generate a default filename based on content type and index
        if let Some(content_type) = part.get_content_type() {
            let extension = self.get_extension_from_content_type(&content_type.c_type);
            format!("attachment_{}{}", index, extension)
        } else {
            format!("attachment_{}.bin", index)
        }
    }
    
    /// Get file extension from MIME content type
    fn get_extension_from_content_type(&self, content_type: &str) -> &'static str {
        match content_type {
            "image/jpeg" => ".jpg",
            "image/png" => ".png",
            "image/gif" => ".gif",
            "application/pdf" => ".pdf",
            "application/zip" => ".zip",
            "application/msword" => ".doc",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => ".docx",
            "application/vnd.ms-excel" => ".xls",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => ".xlsx",
            "text/plain" => ".txt",
            "text/html" => ".html",
            _ => ".bin", // Default binary extension
        }
    }

    /// Close the IMAP connection
    pub async fn close(self) -> Result<()> {
        if let Some(mut session) = self.session {
            session.logout().await
                .map_err(|e| ImapError::Operation(format!("Logout failed: {:?}", e)))?;
        }
        Ok(())
    }
}

/// Apply message filters to determine if a message should be processed
pub fn should_process_message(
    mail_doc: &MailDocument,
    filter: &MessageFilter,
) -> bool {
    // Check subject keywords
    if !filter.subject_keywords.is_empty() {
        let subject_lower = mail_doc.subject.to_lowercase();
        let has_subject_keyword = filter.subject_keywords.iter()
            .any(|keyword| subject_lower.contains(&keyword.to_lowercase()));
        if !has_subject_keyword {
            return false;
        }
    }

    // Check sender keywords
    if !filter.sender_keywords.is_empty() {
        let has_sender_keyword = mail_doc.from.iter()
            .any(|from_addr| {
                let from_lower = from_addr.to_lowercase();
                filter.sender_keywords.iter()
                    .any(|keyword| from_lower.contains(&keyword.to_lowercase()))
            });
        if !has_sender_keyword {
            return false;
        }
    }

    // Check recipient keywords
    if !filter.recipient_keywords.is_empty() {
        let has_recipient_keyword = mail_doc.to.iter()
            .any(|to_addr| {
                let to_lower = to_addr.to_lowercase();
                filter.recipient_keywords.iter()
                    .any(|keyword| to_lower.contains(&keyword.to_lowercase()))
            });
        if !has_recipient_keyword {
            return false;
        }
    }

    true
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::MessageFilter;

    #[test]
    fn test_message_filtering() {
        let mail_doc = MailDocument::new(
            "123".to_string(),
            "INBOX".to_string(),
            vec!["sender@example.com".to_string()],
            vec!["recipient@test.com".to_string()],
            "Urgent: Meeting tomorrow".to_string(),
            Utc::now(),
            "Test body".to_string(),
            HashMap::new(),
            false,
        );

        // Test subject keyword filtering
        let mut filter = MessageFilter {
            subject_keywords: vec!["urgent".to_string()],
            ..Default::default()
        };
        assert!(should_process_message(&mail_doc, &filter));

        filter.subject_keywords = vec!["spam".to_string()];
        assert!(!should_process_message(&mail_doc, &filter));

        // Test sender keyword filtering
        filter = MessageFilter {
            sender_keywords: vec!["@example.com".to_string()],
            ..Default::default()
        };
        assert!(should_process_message(&mail_doc, &filter));

        filter.sender_keywords = vec!["@spam.com".to_string()];
        assert!(!should_process_message(&mail_doc, &filter));
    }

    #[test]
    fn test_rfc822_parsing() {
        let client = ImapClient {
            session: None,
            source: MailSource {
                name: "test".to_string(),
                enabled: true,
                protocol: "imap".to_string(),
                host: "localhost".to_string(),
                port: 143,
                user: "test".to_string(),
                password: "test".to_string(),
                mode: "archive".to_string(),
                folder_filter: Default::default(),
                message_filter: Default::default(),
            },
        };

        // Test email parsing with the new mail-parser implementation
        // This test needs to be updated to use actual message parsing
        // For now, we'll skip the detailed test since it requires a full email message
    }
}