//! Folder and message filtering functionality for mail2couch
//! 
//! This module provides filtering logic for determining which folders and messages
//! should be processed, matching the behavior of the Go implementation.

use crate::config::FolderFilter;
use anyhow::Result;
use glob::Pattern;
use std::collections::HashSet;

/// Check if a folder should be processed based on folder filters
pub fn should_process_folder(folder_name: &str, filter: &FolderFilter) -> bool {
    // If no include patterns, include all folders by default
    let included = if filter.include.is_empty() {
        true
    } else {
        filter.include.iter().any(|pattern| matches_pattern(folder_name, pattern))
    };

    // If included, check if it's excluded
    if included {
        !filter.exclude.iter().any(|pattern| matches_pattern(folder_name, pattern))
    } else {
        false
    }
}

/// Check if a folder name matches a wildcard pattern
/// Supports * (any characters), ? (single character), and [abc] (character class)
fn matches_pattern(folder_name: &str, pattern: &str) -> bool {
    // Handle exact match first
    if pattern == folder_name {
        return true;
    }

    // Use glob pattern matching
    match Pattern::new(pattern) {
        Ok(glob_pattern) => glob_pattern.matches(folder_name),
        Err(_) => {
            // If pattern compilation fails, fall back to exact match
            pattern == folder_name
        }
    }
}

/// Apply folder filters to a list of folders and return the filtered list
pub fn filter_folders(folders: &[String], filter: &FolderFilter) -> Vec<String> {
    folders.iter()
        .filter(|folder| should_process_folder(folder, filter))
        .cloned()
        .collect()
}

/// Expand wildcard patterns to match actual folder names
/// This is useful for validating patterns against available folders
pub fn expand_patterns(patterns: &[String], available_folders: &[String]) -> Result<HashSet<String>> {
    let mut expanded = HashSet::new();

    for pattern in patterns {
        let matches: Vec<_> = available_folders.iter()
            .filter(|folder| matches_pattern(folder, pattern))
            .cloned()
            .collect();

        if matches.is_empty() {
            log::warn!("Pattern '{}' matches no folders", pattern);
        } else {
            log::debug!("Pattern '{}' matches: {:?}", pattern, matches);
            expanded.extend(matches);
        }
    }

    Ok(expanded)
}

/// Validate folder filter patterns against available folders
/// Returns warnings for patterns that don't match any folders
pub fn validate_folder_patterns(filter: &FolderFilter, available_folders: &[String]) -> Vec<String> {
    let mut warnings = Vec::new();

    // Check include patterns
    for pattern in &filter.include {
        let matches = available_folders.iter()
            .any(|folder| matches_pattern(folder, pattern));
        
        if !matches {
            warnings.push(format!("Include pattern '{}' matches no folders", pattern));
        }
    }

    // Check exclude patterns
    for pattern in &filter.exclude {
        let matches = available_folders.iter()
            .any(|folder| matches_pattern(folder, pattern));
        
        if !matches {
            warnings.push(format!("Exclude pattern '{}' matches no folders", pattern));
        }
    }

    warnings
}

/// Get a summary of folder filtering results
pub fn get_filter_summary(
    all_folders: &[String], 
    filtered_folders: &[String], 
    filter: &FolderFilter
) -> String {
    let total_count = all_folders.len();
    let filtered_count = filtered_folders.len();
    let excluded_count = total_count - filtered_count;

    let mut summary = format!(
        "Folder filtering: {} total, {} selected, {} excluded",
        total_count, filtered_count, excluded_count
    );

    if !filter.include.is_empty() {
        summary.push_str(&format!(" (include: {:?})", filter.include));
    }

    if !filter.exclude.is_empty() {
        summary.push_str(&format!(" (exclude: {:?})", filter.exclude));
    }

    summary
}

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

    fn create_test_folders() -> Vec<String> {
        vec![
            "INBOX".to_string(),
            "Sent".to_string(),
            "Drafts".to_string(),
            "Trash".to_string(),
            "Work/Projects".to_string(),
            "Work/Archive".to_string(),
            "Work/Temp".to_string(),
            "Personal/Family".to_string(),
            "Personal/Finance".to_string(),
            "[Gmail]/Spam".to_string(),
            "[Gmail]/Trash".to_string(),
        ]
    }

    #[test]
    fn test_wildcard_matching() {
        assert!(matches_pattern("INBOX", "*"));
        assert!(matches_pattern("INBOX", "INBOX"));
        assert!(matches_pattern("Work/Projects", "Work/*"));
        assert!(matches_pattern("Work/Projects", "*/Projects"));
        assert!(matches_pattern("Work/Archive", "Work/A*"));
        assert!(matches_pattern("Sent", "?ent"));
        
        assert!(!matches_pattern("INBOX", "Sent"));
        assert!(!matches_pattern("Work/Projects", "Personal/*"));
        assert!(!matches_pattern("INBOX", "??"));
    }

    #[test]
    fn test_folder_filtering_include_all() {
        let folders = create_test_folders();
        let filter = FolderFilter {
            include: vec!["*".to_string()],
            exclude: vec!["Trash".to_string(), "*Temp*".to_string()],
        };

        let filtered = filter_folders(&folders, &filter);
        
        assert!(filtered.contains(&"INBOX".to_string()));
        assert!(filtered.contains(&"Work/Projects".to_string()));
        assert!(!filtered.contains(&"Trash".to_string()));
        assert!(!filtered.contains(&"Work/Temp".to_string()));
    }

    #[test]
    fn test_folder_filtering_specific() {
        let folders = create_test_folders();
        let filter = FolderFilter {
            include: vec!["INBOX".to_string(), "Sent".to_string(), "Work/*".to_string()],
            exclude: vec!["*Temp*".to_string()],
        };

        let filtered = filter_folders(&folders, &filter);
        
        assert!(filtered.contains(&"INBOX".to_string()));
        assert!(filtered.contains(&"Sent".to_string()));
        assert!(filtered.contains(&"Work/Projects".to_string()));
        assert!(filtered.contains(&"Work/Archive".to_string()));
        assert!(!filtered.contains(&"Work/Temp".to_string()));
        assert!(!filtered.contains(&"Drafts".to_string()));
        assert!(!filtered.contains(&"Personal/Family".to_string()));
    }

    #[test]
    fn test_folder_filtering_no_include() {
        let folders = create_test_folders();
        let filter = FolderFilter {
            include: vec![],
            exclude: vec!["Trash".to_string(), "[Gmail]/*".to_string()],
        };

        let filtered = filter_folders(&folders, &filter);
        
        // Should include everything except excluded
        assert!(filtered.contains(&"INBOX".to_string()));
        assert!(filtered.contains(&"Work/Projects".to_string()));
        assert!(!filtered.contains(&"Trash".to_string()));
        assert!(!filtered.contains(&"[Gmail]/Spam".to_string()));
        assert!(!filtered.contains(&"[Gmail]/Trash".to_string()));
    }

    #[test]
    fn test_pattern_expansion() {
        let folders = create_test_folders();
        let patterns = vec!["Work/*".to_string(), "Personal/*".to_string()];
        
        let expanded = expand_patterns(&patterns, &folders).unwrap();
        
        assert!(expanded.contains("Work/Projects"));
        assert!(expanded.contains("Work/Archive"));
        assert!(expanded.contains("Work/Temp"));
        assert!(expanded.contains("Personal/Family"));
        assert!(expanded.contains("Personal/Finance"));
        assert!(!expanded.contains("INBOX"));
        assert!(!expanded.contains("Sent"));
    }

    #[test]
    fn test_filter_validation() {
        let folders = create_test_folders();
        let filter = FolderFilter {
            include: vec!["INBOX".to_string(), "NonExistent/*".to_string()],
            exclude: vec!["Trash".to_string(), "AnotherNonExistent".to_string()],
        };

        let warnings = validate_folder_patterns(&filter, &folders);
        
        assert_eq!(warnings.len(), 2);
        assert!(warnings.iter().any(|w| w.contains("NonExistent/*")));
        assert!(warnings.iter().any(|w| w.contains("AnotherNonExistent")));
    }

    #[test]
    fn test_filter_summary() {
        let folders = create_test_folders();
        let filter = FolderFilter {
            include: vec!["*".to_string()],
            exclude: vec!["Trash".to_string(), "*Temp*".to_string()],
        };
        let filtered = filter_folders(&folders, &filter);

        let summary = get_filter_summary(&folders, &filtered, &filter);
        
        assert!(summary.contains(&format!("{} total", folders.len())));
        assert!(summary.contains(&format!("{} selected", filtered.len())));
        assert!(summary.contains("exclude:"));
    }
}