use crate::{DbnestError, Result};
use std::io::ErrorKind;
use std::process::{Command, Output};

pub fn run_docker(args: &[&str], hint: &str) -> Result<Output> {
    let command = format!("docker {}", args.join(" "));
    run_docker_with_display(args, &command, hint)
}

pub fn run_docker_with_display(args: &[&str], display_command: &str, hint: &str) -> Result<Output> {
    let out = Command::new("docker").args(args).output().map_err(|err| {
        if err.kind() == ErrorKind::NotFound {
            DbnestError::DockerNotAvailable
        } else {
            DbnestError::Io(err)
        }
    })?;

    if out.status.success() {
        return Ok(out);
    }

    let stderr = String::from_utf8_lossy(&out.stderr).trim().to_string();
    let hint = docker_failure_hint(&stderr, hint);

    Err(DbnestError::DockerCommandFailed {
        command: display_command.to_string(),
        stderr,
        hint,
    })
}

pub(crate) fn docker_failure_hint(stderr: &str, default_hint: &str) -> String {
    let lower = stderr.to_ascii_lowercase();

    if lower.contains("permission denied") {
        return "Docker is installed but the current user cannot access the Docker daemon. Check Docker group membership or daemon permissions.".into();
    }

    if lower.contains("cannot connect to the docker daemon")
        || lower.contains("failed to connect to the docker api")
        || lower.contains("is the docker daemon running")
        || lower.contains("dial unix")
    {
        return "Docker is installed but the daemon is not reachable. Start Docker Desktop or the Docker daemon, and check user permissions.".into();
    }

    default_hint.to_string()
}

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

    #[test]
    fn classifies_daemon_unreachable_errors() {
        let hint = docker_failure_hint(
            "failed to connect to the docker API at unix:///var/run/docker.sock: dial unix /var/run/docker.sock: connect: no such file or directory",
            "default",
        );

        assert!(hint.contains("daemon is not reachable"));
    }

    #[test]
    fn classifies_permission_errors() {
        let hint = docker_failure_hint(
            "permission denied while trying to connect to the Docker daemon socket",
            "default",
        );

        assert!(hint.contains("current user cannot access"));
    }

    #[test]
    fn preserves_default_hint_for_other_errors() {
        assert_eq!(
            docker_failure_hint("No such container", "default"),
            "default"
        );
    }
}
