#![allow(clippy::pedantic, clippy::nursery, clippy::cargo)]

use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;

use rshogi::board::{self, perft};

#[derive(Debug, Clone)]
struct PositionInput {
    label: String,
    sfen: String,
}

fn load_positions(path: &PathBuf) -> Result<Vec<PositionInput>, Box<dyn std::error::Error>> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let mut positions = Vec::new();

    for line in reader.lines() {
        let line = line?;
        let trimmed = line.trim();
        if trimmed.is_empty() || trimmed.starts_with('#') {
            continue;
        }
        let Some((label, sfen)) = trimmed.split_once(',') else {
            continue;
        };
        let label = label.trim();
        let sfen = sfen.trim();
        if !label.is_empty() && !sfen.is_empty() {
            positions.push(PositionInput { label: label.to_string(), sfen: sfen.to_string() });
        }
    }

    Ok(positions)
}

fn parse_args() -> Result<(u8, PathBuf), Box<dyn std::error::Error>> {
    let mut depth: Option<u8> = None;
    let mut positions_path: Option<PathBuf> = None;

    let mut args = env::args().skip(1);
    while let Some(arg) = args.next() {
        match arg.as_str() {
            "--depth" => {
                let value = args.next().ok_or("--depth requires a value")?;
                depth = Some(value.parse()?);
            }
            "--positions" => {
                let value = args.next().ok_or("--positions requires a value")?;
                positions_path = Some(PathBuf::from(value));
            }
            _ => {}
        }
    }

    let depth = depth.unwrap_or(3);
    let positions_path =
        positions_path.unwrap_or_else(|| PathBuf::from("data/bench/representative_positions.csv"));
    Ok((depth, positions_path))
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (depth, positions_path) = parse_args()?;
    let positions = load_positions(&positions_path)?;
    if positions.is_empty() {
        return Err("no positions loaded from CSV".into());
    }

    println!("engine,rshogi");
    println!("label,nodes,seconds,nps");

    let mut total_nodes: u64 = 0;
    let mut total_seconds: f64 = 0.0;

    for pos in positions {
        let position =
            board::position_from_sfen(&pos.sfen).map_err(|e| format!("invalid sfen: {e}"))?;
        let stats = perft::perft_bench(&position, depth)?;
        let seconds = stats.elapsed.as_secs_f64();
        total_nodes += stats.nodes;
        total_seconds += seconds;
        println!("{},{},{:.6},{}", pos.label, stats.nodes, seconds, stats.nps);
    }

    let total_nps = if total_seconds > 0.0 { (total_nodes as f64) / total_seconds } else { 0.0 };
    println!("total,{},{:.6},{:.2}", total_nodes, total_seconds, total_nps);

    Ok(())
}
