use clap::{command, value_parser, Arg, ArgAction};
use rand::RngExt;
use std::io::{self, Write};

mod fake_gen;

const AVAILABLE_LOCALES: [&str; 14] = [
    "en", "fr_fr", "zh_tw", "zh_cn", "ja_jp", "ar_sa", "pt_br", "pt_pt", "de_de", "it_it", "cy_gb",
    "nl_nl", "tr_tr", "fa_ir",
];

pub use fake_gen::{all_fakegen_commands, AVAILABLE_LOCALES};
pub fn main() {
    let stdout = io::stdout();
    let mut buf_stdout = io::BufWriter::new(stdout);

    let mut thread_rng = rand::rng();
    let args = cli_parser();

    if !args.0.quiet {
        writeln!(
            buf_stdout,
            "Generating {} fakes for {:?} locale",
            args.0.repeats, args.0.locale
        )
        .unwrap();
    }
    (0..args.0.repeats).for_each(|_| writeln!(buf_stdout, "{}", args.1(&mut thread_rng)).unwrap());
}

impl TryFrom<&str> for AVAILABLE_LOCALES {
    type Error = String;
    fn try_from(str_val: &str) -> Result<Self, Self::Error> {
        let str_val = str_val.to_lowercase();
        let variant =  match str_val.as_str(){
            "en" => AVAILABLE_LOCALES::EN,
            "fr_fr" => AVAILABLE_LOCALES::FR_FR,
            "zh_tw" => AVAILABLE_LOCALES::ZH_TW,
            "zh_cn" => AVAILABLE_LOCALES::ZH_CN,
            "ja_jp" => AVAILABLE_LOCALES::JA_JP,
            "ar_sa" => AVAILABLE_LOCALES::AR_SA,
            "pt_br" => AVAILABLE_LOCALES::PT_BR,
            "pt_pt" => AVAILABLE_LOCALES::PT_PT,
            "de_de" => AVAILABLE_LOCALES::DE_DE,
            "it_it" => AVAILABLE_LOCALES::IT_IT,
            "cy_gb" => AVAILABLE_LOCALES::CY_GB,
            "nl_nl" => AVAILABLE_LOCALES::NL_NL,
            "tr_tr" => AVAILABLE_LOCALES::TR_TR,
            "fa_ir" => AVAILABLE_LOCALES::FA_IR,
            _=> return Err(format!("{} is either an invalid locale or not yet supported.\n The supported locales are: {:?}",str_val,AVAILABLE_LOCALES))
        };
        Ok(variant)
    }
}

fn cli_parser<R: RngExt + ?Sized>() -> (Args, impl Fn(&mut R) -> String) {
    let (subcommands, fake_generator) = all_fakegen_commands::<R>();
    let mut command = command!()
        .arg(
            Arg::new("repeat")
                .long("repeat")
                .short('r')
                .default_value("1")
                .value_parser(value_parser!(u32)),
        )
        .arg(
            Arg::new("locale")
                .short('l')
                .long("locale")
                .default_value("EN")
                .value_parser(|value: &str| AVAILABLE_LOCALES::try_from(value)),
        )
        .arg(
            Arg::new("quiet")
                .short('q')
                .long("quiet")
                .value_parser(value_parser!(bool))
                .action(ArgAction::SetTrue),
        )
        .subcommands(subcommands)
        .arg_required_else_help(true);
    let help_message = command.render_help();
    let matches = command.get_matches();
    let repeats = *matches.get_one::<u32>("repeat").unwrap();
    let locale = matches
        .get_one::<AVAILABLE_LOCALES>("locale")
        .unwrap()
        .to_owned();
    let quiet = matches.get_flag("quiet");
    let fake_gen = fake_generator(matches, locale, help_message);
    (
        Args {
            repeats,
            locale,
            quiet,
        },
        fake_gen,
    )
}

struct Args {
    repeats: u32,
    locale: AVAILABLE_LOCALES,
    quiet: bool,
}
