use std::process::Command;

use anyhow::{bail, format_err, Error};

fn run_command(mut command: Command) -> Result<(), Error> {
    let output = command
        .output()
        .map_err(|err| format_err!("failed to execute {:?} - {}", command, err))?;

    proxmox::try_block!({
        if !output.status.success() {
            match output.status.code() {
                Some(code) => {
                    if code != 0 {
                        let msg = String::from_utf8(output.stderr)
                            .map(|m| {
                                if m.is_empty() {
                                    String::from("no error message")
                                } else {
                                    m
                                }
                            })
                            .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));

                        bail!("status code: {} - {}", code, msg);
                    }
                }
                None => bail!("terminated by signal"),
            }
        }
        Ok(())
    }).map_err(|err| format_err!("command {:?} failed - {}", command, err))?;

    Ok(())
}

pub fn reload_daemon() -> Result<(), Error> {
    let mut command = std::process::Command::new("systemctl");
    command.arg("daemon-reload");

    run_command(command)?;

    Ok(())
}

pub fn disable_unit(unit: &str) -> Result<(), Error> {
    let mut command = std::process::Command::new("systemctl");
    command.arg("disable");
    command.arg(unit);

    run_command(command)?;

    Ok(())
}

pub fn enable_unit(unit: &str) -> Result<(), Error> {
    let mut command = std::process::Command::new("systemctl");
    command.arg("enable");
    command.arg(unit);

    run_command(command)?;

    Ok(())
}

pub fn start_unit(unit: &str) -> Result<(), Error> {
    let mut command = std::process::Command::new("systemctl");
    command.arg("start");
    command.arg(unit);

    run_command(command)?;

    Ok(())
}

pub fn stop_unit(unit: &str) -> Result<(), Error> {
    let mut command = std::process::Command::new("systemctl");
    command.arg("stop");
    command.arg(unit);

    run_command(command)?;

    Ok(())
}

pub fn reload_unit(unit: &str) -> Result<(), Error> {
    let mut command = std::process::Command::new("systemctl");
    command.arg("try-reload-or-restart");
    command.arg(unit);

    run_command(command)?;

    Ok(())
}

#[test]
fn test_escape_unit() -> Result<(), Error> {
    fn test_escape(i: &str, expected: &str, is_path: bool) {

        use proxmox::tools::systemd::{escape_unit, unescape_unit};

        let escaped = escape_unit(i, is_path);
        assert_eq!(escaped, expected);
        let unescaped = unescape_unit(&escaped).unwrap();
        if is_path {
            let mut p = i.trim_matches('/');
            if p.is_empty() {
                p = "/";
            }
            assert_eq!(p, unescaped);
        } else {
            assert_eq!(i, unescaped);
        }
    }

    test_escape(".test", "\\x2etest", false);
    test_escape("t.est", "t.est", false);
    test_escape("_test_", "_test_", false);

    test_escape("/", "-", false);
    test_escape("//", "--", false);

    test_escape("/", "-", true);
    test_escape("//", "-", true);

    Ok(())
}
