extern crate clap;

use clap::builder::{PossibleValuesParser, TypedValueParser};
use clap::{Arg, ArgAction};
use std::path::Path;

use crate::cache::restore::{restore, CacheRestoreOptions};
use crate::commands::engine::*;
use crate::commands::utils::*;
use crate::commands::Command;
use crate::report::{parse_log_level, verbose_args};
use crate::version::*;

pub struct CacheRestoreCommand;

impl CacheRestoreCommand {
    fn cli(&self) -> clap::Command {
        let cmd = clap::Command::new(self.name())
            .next_display_order(None)
            .version(crate::tools_version!())
            .disable_version_flag(true)
            .about("Convert XML format metadata to binary.")
            .arg(
                Arg::new("QUIET")
                    .help("Suppress output messages, return only exit code")
                    .short('q')
                    .long("quiet")
                    .action(ArgAction::SetTrue),
            )
            .arg(
                Arg::new("OMIT_CLEAN_SHUTDOWN")
                    .help("Don't set the clean shutdown flag")
                    .long("omit-clean-shutdown")
                    .action(ArgAction::SetTrue),
            )
            // options
            .arg(
                Arg::new("INPUT")
                    .help("Specify the input xml")
                    .short('i')
                    .long("input")
                    .value_name("FILE")
                    .required(true),
            )
            .arg(
                Arg::new("METADATA_VERSION")
                    .help("Specify the output metadata version")
                    .long("metadata-version")
                    .value_name("NUM")
                    .value_parser(
                        PossibleValuesParser::new(["1", "2"]).map(|s| s.parse::<u8>().unwrap()),
                    )
                    .default_value("2"),
            )
            .arg(
                Arg::new("OUTPUT")
                    .help("Specify the output device")
                    .short('o')
                    .long("output")
                    .value_name("FILE")
                    .required(true),
            );
        verbose_args(engine_args(version_args(cmd)))
    }
}

impl<'a> Command<'a> for CacheRestoreCommand {
    fn name(&self) -> &'a str {
        "cache_restore"
    }

    fn run(&self, args: &mut dyn Iterator<Item = std::ffi::OsString>) -> exitcode::ExitCode {
        let matches = self.cli().get_matches_from(args);
        display_version(&matches);

        let input_file = Path::new(matches.get_one::<String>("INPUT").unwrap());
        let output_file = Path::new(matches.get_one::<String>("OUTPUT").unwrap());

        let report = mk_report(matches.get_flag("QUIET"));
        let log_level = match parse_log_level(&matches) {
            Ok(level) => level,
            Err(e) => return to_exit_code::<()>(&report, Err(anyhow::Error::msg(e))),
        };
        report.set_level(log_level);

        if let Err(e) = check_input_file(input_file).and_then(|_| check_output_file(output_file)) {
            return to_exit_code::<()>(&report, Err(e));
        }

        let engine_opts = parse_engine_opts(ToolType::Cache, &matches);
        if engine_opts.is_err() {
            return to_exit_code(&report, engine_opts);
        }

        let opts = CacheRestoreOptions {
            input: input_file,
            output: output_file,
            metadata_version: *matches.get_one::<u8>("METADATA_VERSION").unwrap(),
            engine_opts: engine_opts.unwrap(),
            report: report.clone(),
            omit_clean_shutdown: matches.get_flag("OMIT_CLEAN_SHUTDOWN"),
        };

        to_exit_code(&report, restore(opts))
    }
}
