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

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

pub struct DockerPlugin;

impl DockerPlugin {
    fn docker_available() -> bool {
        // Quick check: see if the docker socket exists before running a command.
        // This avoids a multi-second timeout when Docker Desktop isn't running.
        let socket_exists = std::path::Path::new("/var/run/docker.sock").exists()
            || dirs::home_dir()
                .map(|h| h.join(".docker/run/docker.sock").exists())
                .unwrap_or(false);

        if !socket_exists {
            return false;
        }

        Command::new("docker")
            .arg("info")
            .stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null())
            .status()
            .map(|s| s.success())
            .unwrap_or(false)
    }

    /// Returns (type_name, reclaimable_bytes) for each Docker resource category.
    fn docker_disk_usage_by_type() -> Vec<(String, u64)> {
        let output = Command::new("docker")
            .args(["system", "df", "--format", "{{.Type}}\t{{.Reclaimable}}"])
            .output();

        match output {
            Ok(out) if out.status.success() => {
                let text = String::from_utf8_lossy(&out.stdout);
                text.lines()
                    .filter_map(|line| {
                        let mut parts = line.splitn(2, '\t');
                        let type_name = parts.next()?.trim().to_string();
                        let reclaimable = parts.next()?.trim();
                        let size_part = reclaimable.split('(').next()?.trim();
                        let size = parse_docker_size(size_part)?;
                        Some((type_name, size))
                    })
                    .collect()
            }
            _ => Vec::new(),
        }
    }
}

fn parse_docker_size(s: &str) -> Option<u64> {
    let s = s.trim().to_uppercase();
    if s == "0B" || s == "0" {
        return Some(0);
    }

    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 {
        "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)
}

impl Plugin for DockerPlugin {
    fn name(&self) -> &str {
        "docker"
    }

    fn description(&self) -> &str {
        "Docker unused images, containers, volumes, and build cache"
    }

    fn is_available(&self) -> bool {
        Self::docker_available()
    }

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

    fn global_targets(&self) -> Vec<CleanTarget> {
        let categories = Self::docker_disk_usage_by_type();
        let mut targets = Vec::new();

        for (type_name, size) in categories {
            if size == 0 {
                continue;
            }

            let (label, command_args, safety, explanation) = match type_name.as_str() {
                "Images" => (
                    "docker image prune -a",
                    vec!["image", "prune", "-a", "-f"],
                    Safety::Unsafe,
                    "Unused container images, re-downloadable with `docker pull`",
                ),
                "Containers" => (
                    "docker container prune",
                    vec!["container", "prune", "-f"],
                    Safety::Unsafe,
                    "Stopped containers and their writable layers",
                ),
                "Local Volumes" => (
                    "docker volume prune -a",
                    vec!["volume", "prune", "-a", "-f"],
                    Safety::Unsafe,
                    "Persistent data volumes, may contain database data or app state",
                ),
                "Build Cache" => (
                    "docker builder prune -a",
                    vec!["builder", "prune", "-a", "-f"],
                    Safety::Recoverable,
                    "Build layer cache, rebuilt automatically on next `docker build`",
                ),
                _ => continue,
            };

            targets.push(CleanTarget {
                label: label.to_string(),
                size_bytes: size,
                action: CleanAction::RunCommand {
                    command: "docker".to_string(),
                    args: command_args.iter().map(|s| s.to_string()).collect(),
                    description: label.to_string(),
                },
                plugin_name: self.name().to_string(),
                safety,
                explanation: explanation.to_string(),
            });
        }

        targets
    }
}
