use std::path::Path;

use crate::plugin::{CleanAction, CleanTarget, Plugin, Safety};
use crate::utils;

pub struct NixPlugin;

impl Plugin for NixPlugin {
    fn name(&self) -> &str {
        "nix"
    }

    fn description(&self) -> &str {
        "Nix store garbage and caches"
    }

    fn scan_directory(&self, _dir: &Path) -> Vec<CleanTarget> {
        Vec::new()
    }

    fn global_targets(&self) -> Vec<CleanTarget> {
        let mut targets = Vec::new();
        let home = utils::home_dir();

        // Nix store garbage collection (unreachable store paths)
        let nix_store = Path::new("/nix/store");
        if nix_store.is_dir() {
            let size = nix_gc_dead_size();
            if size > 0 {
                targets.push(CleanTarget {
                    label: "nix store garbage (unreachable paths)".to_string(),
                    size_bytes: size,
                    action: CleanAction::RunCommand {
                        command: "nix-collect-garbage".to_string(),
                        args: vec!["-d".to_string()],
                        description: "nix-collect-garbage -d".to_string(),
                    },
                    plugin_name: self.name().to_string(),
                    safety: Safety::Recoverable,
                    explanation: "Nix store garbage, unreferenced packages".to_string(),
                });
            }
        }

        // User-level fetcher/tarball cache
        let nix_cache = home.join(".cache/nix");
        if nix_cache.is_dir() {
            let size = utils::dir_size(&nix_cache);
            targets.push(CleanTarget {
                label: format!("{}", nix_cache.display()),
                size_bytes: size,
                action: CleanAction::DeletePath(nix_cache),
                plugin_name: self.name().to_string(),
                safety: Safety::Recoverable,
                explanation: "Nix evaluation cache, regenerated automatically".to_string(),
            });
        }

        // Nix daemon log (macOS)
        let nix_log = Path::new("/nix/var/log/nix");
        if nix_log.is_dir() {
            let size = utils::dir_size(nix_log);
            targets.push(CleanTarget {
                label: format!("{}", nix_log.display()),
                size_bytes: size,
                action: CleanAction::DeletePath(nix_log.to_path_buf()),
                plugin_name: self.name().to_string(),
                safety: Safety::Recoverable,
                explanation: "Nix build logs".to_string(),
            });
        }

        targets
    }

    fn is_available(&self) -> bool {
        Path::new("/nix").is_dir()
    }
}

/// Estimate the size of dead store paths by running `nix-store --gc --print-dead`.
fn nix_gc_dead_size() -> u64 {
    use std::process::Command;

    let output = Command::new("nix-store")
        .args(["--gc", "--print-dead"])
        .output();

    match output {
        Ok(out) if out.status.success() => {
            let text = String::from_utf8_lossy(&out.stdout);
            text.lines()
                .filter_map(|line| {
                    let path = Path::new(line.trim());
                    path.metadata().map(|m| utils::physical_size(&m)).ok()
                })
                .sum()
        }
        _ => 0,
    }
}
