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

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

pub struct HomebrewPlugin;

impl Plugin for HomebrewPlugin {
    fn name(&self) -> &str {
        "homebrew"
    }

    fn description(&self) -> &str {
        "Homebrew download cache and old package versions"
    }

    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();

        // Download cache
        let cache_dir = home.join("Library/Caches/Homebrew");
        if cache_dir.is_dir() {
            let size = utils::dir_size(&cache_dir);
            targets.push(CleanTarget {
                label: format!("{}", cache_dir.display()),
                size_bytes: size,
                action: CleanAction::DeletePath(cache_dir),
                plugin_name: self.name().to_string(),
                safety: Safety::Recoverable,
                explanation: "Homebrew download cache, re-downloaded as needed".to_string(),
            });
        }

        // `brew cleanup` removes old versions and stale downloads
        let cleanup_size = brew_cleanup_size();
        if cleanup_size > 0 {
            targets.push(CleanTarget {
                label: "brew cleanup (old versions & stale downloads)".to_string(),
                size_bytes: cleanup_size,
                action: CleanAction::RunCommand {
                    command: "brew".to_string(),
                    args: vec!["cleanup".to_string(), "--prune=all".to_string()],
                    description: "brew cleanup --prune=all".to_string(),
                },
                plugin_name: self.name().to_string(),
                safety: Safety::Recoverable,
                explanation: "Homebrew cleanup of outdated downloads and old versions".to_string(),
            });
        }

        targets
    }

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

/// Parse the "This operation would free approximately X" line from `brew cleanup -n`.
fn brew_cleanup_size() -> u64 {
    let output = Command::new("brew").args(["cleanup", "-n"]).output();

    match output {
        Ok(out) if out.status.success() => {
            let text = String::from_utf8_lossy(&out.stdout);
            // Look for the summary line: "==> This operation would free approximately 3.7GB"
            text.lines()
                .find(|line| line.contains("would free approximately"))
                .and_then(|line| {
                    let size_str = line.rsplit("approximately ").next()?.trim();
                    parse_human_size(size_str)
                })
                .unwrap_or(0)
        }
        _ => 0,
    }
}

fn parse_human_size(s: &str) -> Option<u64> {
    let s = s.trim();
    let (num_str, unit) = s
        .find(|c: char| !c.is_ascii_digit() && c != '.')
        .map(|i| s.split_at(i))
        .unwrap_or((s, "B"));

    let num: f64 = num_str.parse().ok()?;
    let multiplier: f64 = match unit.to_uppercase().as_str() {
        "B" => 1.0,
        "KB" => 1_000.0,
        "MB" => 1_000_000.0,
        "GB" => 1_000_000_000.0,
        "TB" => 1_000_000_000_000.0,
        _ => return None,
    };

    Some((num * multiplier) as u64)
}
