use std::path::Path;
use std::process::Command;

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

pub struct GitPlugin;

impl Plugin for GitPlugin {
    fn name(&self) -> &str {
        "git"
    }

    fn description(&self) -> &str {
        "Git garbage collection and reflog cleanup"
    }

    fn is_available(&self) -> bool {
        Command::new("git")
            .arg("--version")
            .stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null())
            .status()
            .map(|s| s.success())
            .unwrap_or(false)
    }

    fn scan_directory(&self, dir: &Path) -> Vec<CleanTarget> {
        let mut targets = Vec::new();

        let git_dir = dir.join(".git");
        if git_dir.is_dir() {
            let size = git_gc_reclaimable(dir);
            if size > 0 {
                targets.push(CleanTarget {
                    label: format!("git gc -- {}", dir.display()),
                    size_bytes: size,
                    action: CleanAction::RunCommand {
                        command: "git".to_string(),
                        args: vec![
                            "-C".to_string(),
                            dir.to_string_lossy().to_string(),
                            "gc".to_string(),
                            "--aggressive".to_string(),
                            "--prune=now".to_string(),
                        ],
                        description: format!(
                            "git gc --aggressive --prune=now in {}",
                            dir.display()
                        ),
                    },
                    plugin_name: self.name().to_string(),
                    safety: Safety::Recoverable,
                    explanation: "Git repository garbage collection, optimizes storage".to_string(),
                });
            }
        }

        targets
    }

    fn skip_subdirectories(&self) -> &'static [&'static str] {
        &[".git"]
    }

    fn markers(&self) -> &'static [&'static str] {
        &[".git"]
    }
}

fn git_gc_reclaimable(repo: &Path) -> u64 {
    let output = Command::new("git")
        .args(["-C", &repo.to_string_lossy(), "count-objects", "-v"])
        .output();

    match output {
        Ok(out) if out.status.success() => {
            let text = String::from_utf8_lossy(&out.stdout);
            let mut total_kb = 0u64;
            for line in text.lines() {
                // Only count garbage — these are truly reclaimable unreachable objects.
                // The "size" field includes reachable loose objects that gc will pack
                // but not free, so including it would overestimate reclaimable space.
                if let Some(rest) = line.strip_prefix("size-garbage: ") {
                    total_kb += rest.trim().parse::<u64>().unwrap_or(0);
                }
            }
            total_kb * 1024
        }
        _ => 0,
    }
}
