use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};

use criterion::{Criterion, criterion_group, criterion_main};
use nishikaze::config::load::load_config;
use nishikaze::config::schema::{BuildCfg, FileConfig, PhaseArgsCfg, ProfileCfg, ProjectCfg};
use nishikaze::core::cli::{Cli, Command};
use nishikaze::core::cmd::run;
use nishikaze::core::config::{ConfigCtx, resolve_single};

fn make_temp_dir(label: &str) -> PathBuf {
    static COUNTER: AtomicUsize = AtomicUsize::new(0);
    let mut dir = std::env::temp_dir();
    dir.push(format!(
        "nishikaze-bench-{}-{}",
        label,
        COUNTER.fetch_add(1, Ordering::Relaxed)
    ));
    if dir.exists() {
        std::fs::remove_dir_all(&dir).expect("clean temp dir");
    }
    std::fs::create_dir_all(&dir).expect("create temp dir");
    dir
}

fn write_kaze_toml(project_dir: &Path) {
    let path = project_dir.join("kaze.toml");
    std::fs::write(
        &path,
        r#"[project]
board = "native_sim"
runner = "openocd"

[build]
root = "build"

[args]
conf = ["-DCFG=1"]
build = ["-DBUILD=1"]
flash = ["-DFLASH=1"]
run = ["-DRUN=1"]

[profile.dev]
runner = "jlink"
"#,
    )
    .expect("write kaze.toml");
}

fn bench_load_config(c: &mut Criterion) {
    let dir = make_temp_dir("load-config");
    write_kaze_toml(&dir);
    c.bench_function("config/load_config", |b| {
        b.iter(|| load_config(&dir).expect("load config"))
    });
}

fn bench_resolve_single(c: &mut Criterion) {
    let dir = make_temp_dir("resolve-single");
    let cfg = FileConfig {
        project: ProjectCfg {
            board: Some("native_sim".into()),
            runner: Some("openocd".into()),
            default_profile: Some("dev".into()),
            name: None,
            args: PhaseArgsCfg::default(),
        },
        build: BuildCfg {
            root: "build".into(),
            link_compile_commands: None,
        },
        ..FileConfig::default()
    };
    let mut cfg = cfg;
    cfg.profiles.insert("dev".into(), ProfileCfg::default());

    let cli = Cli {
        preclean: false,
        all: false,
        profile: Some("dev".into()),
        board: None,
        runner: None,
        project: None,
        verbose: 0,
        dry_run: false,
        command: Command::Build(Default::default()),
    };
    let ctx = ConfigCtx {
        project_dir: dir.clone(),
        cwd: dir,
        invoked_from_build: false,
    };

    c.bench_function("config/resolve_single", |b| {
        b.iter(|| resolve_single(&cli, &ctx, &cfg).expect("resolve"))
    });
}

fn bench_run_profiles(c: &mut Criterion) {
    let dir = make_temp_dir("cmd-run");
    write_kaze_toml(&dir);
    let cli = Cli {
        preclean: false,
        all: false,
        profile: None,
        board: None,
        runner: None,
        project: Some(dir),
        verbose: 0,
        dry_run: true,
        command: Command::Profiles,
    };

    c.bench_function("cmd/run_profiles", |b| {
        b.iter(|| run(&cli).expect("run profiles"))
    });
}

criterion_group!(
    benches,
    bench_load_config,
    bench_resolve_single,
    bench_run_profiles
);
criterion_main!(benches);
