#![warn(clippy::all)]
// #![warn(missing_docs)]
#![warn(missing_debug_implementations)]

use std::thread::sleep;

use clap::Parser;
use libpt::log::*;

use wordle_analyzer::bench::builtin::BuiltinBenchmark;
use wordle_analyzer::bench::{Benchmark, DEFAULT_N};
use wordle_analyzer::game::GameBuilder;
use wordle_analyzer::solve::{AnyBuiltinSolver, BuiltinSolverNames};
use wordle_analyzer::wlist::builtin::BuiltinWList;

use wordle_analyzer::{self, game};

#[derive(Parser, Clone, Debug)]
#[command(version, about, long_about, author)]
struct Cli {
    /// precompute all possibilities for better performance at runtime
    #[arg(short, long)]
    precompute: bool,
    /// how long should the word be?
    #[arg(short, long, default_value_t = wordle_analyzer::DEFAULT_WORD_LENGTH)]
    length: usize,
    /// how many times can we guess?
    #[arg(short, long, default_value_t = wordle_analyzer::DEFAULT_MAX_STEPS)]
    max_steps: usize,
    /// more verbose logs
    #[arg(short, long)]
    verbose: bool,
    /// which solver to use
    #[arg(short, long, default_value_t = BuiltinSolverNames::default())]
    solver: BuiltinSolverNames,
    /// how many games to play for the benchmark
    #[arg(short, long, default_value_t = DEFAULT_N)]
    n: usize,
    /// how many threads to use for benchmarking
    ///
    /// Note that the application as the whole will use at least one more thread.
    #[arg(short, long, default_value_t = num_cpus::get())]
    threads: usize,

    /// select a wordlist
    ///
    /// 'ger' and 'eng' are special values bundled with this executable, if the value does not
    /// match either of those, it will be assumed to be a file path.
    #[arg(short, long, default_value_t = String::from("eng"))]
    wordlist: String,
}

fn main() -> anyhow::Result<()> {
    let cli = Cli::parse();
    if cli.verbose {
        Logger::builder().set_level(Level::DEBUG).build().unwrap();
    } else {
        Logger::builder().set_level(Level::INFO).build().unwrap();
    }
    trace!("dumping CLI: {:#?}", cli);

    let wl = match cli.wordlist.as_str() {
        "ger" => BuiltinWList::german(cli.length),
        "eng" => BuiltinWList::english(cli.length),
        _ => BuiltinWList::load(&cli.wordlist, cli.length)?,
    };
    let builder: GameBuilder<'_, BuiltinWList> = game::Game::builder(&wl)
        .length(cli.length)
        .max_steps(cli.max_steps)
        .precompute(cli.precompute);
    let solver: AnyBuiltinSolver<'_, BuiltinWList> = cli.solver.to_solver(&wl);
    let bench = BuiltinBenchmark::build(&wl, solver, builder, cli.threads)?;
    trace!("{bench:#?}");

    bench.start(cli.n, &bench.builder())?;

    loop {
        sleep(std::time::Duration::from_secs(1));
        println!("{}", bench.report());
        if bench.is_finished() {
            break;
        }
    }

    Ok(())
}
