use std::fs;
use std::path::{Path, PathBuf};

#[derive(Debug, Clone)]
pub struct OrphanedDesktopEntry {
    pub desktop_file_path: PathBuf,
    pub original_appimage_path: String,
    pub app_name: Option<String>,
}

pub struct CleanupSummary {
    pub entries_removed: usize,
    pub icons_removed: usize,
}

fn applications_dir() -> PathBuf {
    crate::config::data_dir_fallback()
        .join("applications")
}

fn icons_dir() -> PathBuf {
    crate::config::data_dir_fallback()
        .join("icons/hicolor")
}

/// Parse key-value pairs from a .desktop file's [Desktop Entry] section.
fn parse_desktop_key(content: &str, key: &str) -> Option<String> {
    let mut in_section = false;
    for line in content.lines() {
        let line = line.trim();
        if line == "[Desktop Entry]" {
            in_section = true;
            continue;
        }
        if line.starts_with('[') {
            in_section = false;
            continue;
        }
        if !in_section {
            continue;
        }
        if let Some(value) = line.strip_prefix(key).and_then(|rest| rest.strip_prefix('=')) {
            return Some(value.trim().to_string());
        }
    }
    None
}

/// Scan for orphaned desktop entries managed by Driftwood.
pub fn detect_orphans() -> Vec<OrphanedDesktopEntry> {
    let mut orphans = Vec::new();
    let apps_dir = applications_dir();

    let entries = match fs::read_dir(&apps_dir) {
        Ok(entries) => entries,
        Err(_) => return orphans,
    };

    for entry in entries.flatten() {
        let path = entry.path();

        // Only check driftwood-*.desktop files
        let _filename = match path.file_name().and_then(|n| n.to_str()) {
            Some(name) if name.starts_with("driftwood-") && name.ends_with(".desktop") => name,
            _ => continue,
        };

        // Read and check if managed by Driftwood
        let content = match fs::read_to_string(&path) {
            Ok(c) => c,
            Err(_) => continue,
        };

        let managed = parse_desktop_key(&content, "X-AppImage-Managed-By");
        if managed.as_deref() != Some("Driftwood") {
            continue;
        }

        // Check if the referenced AppImage still exists
        let appimage_path = match parse_desktop_key(&content, "X-AppImage-Path") {
            Some(p) => p,
            None => continue,
        };

        if !Path::new(&appimage_path).exists() {
            let app_name = parse_desktop_key(&content, "Name");
            orphans.push(OrphanedDesktopEntry {
                desktop_file_path: path,
                original_appimage_path: appimage_path,
                app_name,
            });
        }
    }

    orphans
}

/// Clean up a specific orphaned desktop entry.
pub fn clean_orphan(entry: &OrphanedDesktopEntry) -> Result<(bool, usize), std::io::Error> {
    let mut icons_removed = 0;

    // Remove the .desktop file
    let entry_removed = if entry.desktop_file_path.exists() {
        fs::remove_file(&entry.desktop_file_path)?;
        true
    } else {
        false
    };

    // Try to determine the icon ID and remove associated icon files
    if let Some(filename) = entry.desktop_file_path.file_stem().and_then(|n| n.to_str()) {
        // filename is like "driftwood-firefox" - the icon ID is the same
        let icon_id = filename;
        let base = icons_dir();
        let candidates = [
            base.join(format!("256x256/apps/{}.png", icon_id)),
            base.join(format!("scalable/apps/{}.svg", icon_id)),
            base.join(format!("128x128/apps/{}.png", icon_id)),
            base.join(format!("48x48/apps/{}.png", icon_id)),
        ];
        for path in &candidates {
            if path.exists() {
                fs::remove_file(path)?;
                icons_removed += 1;
            }
        }
    }

    Ok((entry_removed, icons_removed))
}

/// Clean all detected orphans.
pub fn clean_all_orphans() -> Result<CleanupSummary, std::io::Error> {
    let orphans = detect_orphans();
    let mut summary = CleanupSummary {
        entries_removed: 0,
        icons_removed: 0,
    };

    for entry in &orphans {
        match clean_orphan(entry) {
            Ok((removed, icons)) => {
                if removed {
                    summary.entries_removed += 1;
                }
                summary.icons_removed += icons;
            }
            Err(e) => {
                log::warn!(
                    "Failed to clean orphan {}: {}",
                    entry.desktop_file_path.display(),
                    e
                );
            }
        }
    }

    Ok(summary)
}

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

    #[test]
    fn test_parse_desktop_key() {
        let content = "[Desktop Entry]\n\
                        Name=Test App\n\
                        X-AppImage-Path=/home/user/test.AppImage\n\
                        X-AppImage-Managed-By=Driftwood\n";
        assert_eq!(
            parse_desktop_key(content, "Name"),
            Some("Test App".to_string())
        );
        assert_eq!(
            parse_desktop_key(content, "X-AppImage-Path"),
            Some("/home/user/test.AppImage".to_string())
        );
        assert_eq!(
            parse_desktop_key(content, "X-AppImage-Managed-By"),
            Some("Driftwood".to_string())
        );
        assert_eq!(parse_desktop_key(content, "Missing"), None);
    }

    #[test]
    fn test_parse_desktop_key_ignores_other_sections() {
        let content = "[Desktop Entry]\n\
                        Name=App\n\
                        [Desktop Action New]\n\
                        Name=Other\n";
        assert_eq!(
            parse_desktop_key(content, "Name"),
            Some("App".to_string())
        );
    }
}
