use clap::{ArgMatches, Command};

use qsu::{
  argp::Cmd,
  installer::RegSvc,
  rt::{RunCtx, SrvAppRt}
};

use crate::err::Error;

pub struct AppArgsProc {
  pub(crate) bldr: Box<dyn Fn() -> SrvAppRt<Error>>
}

impl qsu::argp::ArgsProc for AppArgsProc {
  type AppErr = Error;

  fn conf_cmd(
    &mut self,
    cmdtype: Cmd,
    cmd: Command
  ) -> Result<Command, Self::AppErr> {
    let cmd = if cmdtype == Cmd::Root {
      // tip: help_template() can be used to include version number in help
      cmd
        .name(env!("CARGO_BIN_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
    } else {
      cmd
    };
    Ok(cmd)
  }

  /// Process an `register-service` subcommand.
  fn proc_inst(
    &mut self,
    _sub_m: &ArgMatches,
    regsvc: RegSvc
  ) -> Result<RegSvc, Self::AppErr> {
    // This is split out into its own function because the orphan rule wouldn't
    // allow the application to implement a std::io::Error -> qsu::AppErr
    // conversion in one go, so we do it in two steps instead.
    // proc_inst_inner()'s '?' converts "all" errors into 'Error`.
    // The proc_inst() method's `?` converts from `Error` to `qsu::AppError`
    proc_inst_inner(regsvc)
  }

  fn build_apprt(
    &mut self,
    _runctx: &mut RunCtx
  ) -> Result<SrvAppRt<Error>, Self::AppErr> {
    Ok((self.bldr)())
  }
}

fn proc_inst_inner(regsvc: RegSvc) -> Result<RegSvc, Error> {
  // Use current working directory as the service's workdir
  let cwd = std::env::current_dir()?.to_str().unwrap().to_string();
  let mut regsvc = regsvc
    .workdir(cwd)
    .env("HOLY", "COW")
    .env("Private", "Public")
    .env("General", "Specific");

  // Set display name, but only if it wasn't already specified on the command
  // line
  if regsvc.display_name.is_none() {
    regsvc.display_name_ref("Hello Service");
  }

  // Hard-code servie as supporting conf-reload service events.
  let regsvc = regsvc.conf_reload();

  // Add a callback that will increase log and trace levels by deafault.
  #[cfg(windows)]
  let regsvc = regsvc.regconf(|_svcname, params| {
    // Leave a key/value pair in the registry as a hello
    params.set_string("AppArgParser", "SaysHello")?;

    Ok(())
  });

  Ok(regsvc)
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
