use std::{
    env,
    fs::read_dir,
    path::{Path, PathBuf},
    process::{Command, ExitCode},
    str::FromStr,
};

const RC_DIR: &str = "/etc/rc.d";

#[derive(Debug)]
#[allow(dead_code)]
enum Error {
    IncorrectUsage(String),
    IoError(std::io::Error),
}

impl From<std::io::Error> for Error {
    fn from(value: std::io::Error) -> Self {
        Self::IoError(value)
    }
}

#[derive(Debug)]
enum Mode {
    Default,
}

impl FromStr for Mode {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "default" => Ok(Self::Default),
            _ => Err(Error::IncorrectUsage(format!("Incorrect mode: {:?}", s))),
        }
    }
}

fn exec_script<P: AsRef<Path>>(path: P, arg: &str) -> Result<(), Error> {
    let path = path.as_ref();
    log::info!("rc: {:?} {}", path, arg);

    // TODO run those in parallel, if allowed
    let mut process = Command::new(path).arg(arg).spawn()?;

    if !process.wait()?.success() {
        eprintln!("{:?}: Failed", path);
    }

    Ok(())
}

fn get_scripts<P: AsRef<Path>>(dir: P) -> Result<Vec<String>, Error> {
    let mut items = vec![];
    for item in read_dir(dir)? {
        let item = item?;
        if !item.file_type()?.is_file() {
            continue;
        }
        items.push(item.file_name().to_str().unwrap().to_owned());
    }
    Ok(items)
}

fn _main(mode: &str) -> Result<(), Error> {
    // TODO only 1 mode supported, so ignore it
    let _mode = Mode::from_str(mode)?;

    let path = PathBuf::from(RC_DIR);
    let scripts = get_scripts(&path)?;

    // Execute scripts in order
    for script in scripts {
        if let Err(err) = exec_script(path.join(&script), "start") {
            eprintln!("{}: {:?}", script, err);
        }
    }

    Ok(())
}

fn usage(_cmd: &str) {
    eprintln!("Usage: ...");
}

fn main() -> ExitCode {
    let args: Vec<_> = env::args().collect();
    if args.len() != 2 {
        eprintln!("Incorrect rc usage, expected 2 args, got {}", args.len());
        usage(&args[0]);
        return ExitCode::FAILURE;
    }

    match _main(&args[1]) {
        Ok(_) => ExitCode::SUCCESS,
        Err(Error::IncorrectUsage(e)) => {
            eprintln!("rc: incorrect usage: {:?}", e);
            usage(&args[0]);
            ExitCode::FAILURE
        }
        Err(e) => {
            eprintln!("rc: {:?}", e);
            ExitCode::FAILURE
        }
    }
}
