use std::path::Path;

use serde::Deserialize;
use rsstacker::replay::{KeyKind, load_recording};

#[derive(Deserialize)]
struct HandlingFx {
    arr: f64,
    das: f64,
    dcd: i32,
    sdf: i32,
    safelock: bool,
    cancel: bool,
    may20g: bool,
    irs: String,
    ihs: String,
}

#[derive(Deserialize)]
struct MetaCase {
    rel: String,
    replay_id: String,
    gamemode: String,
    seed: i64,
    no_szo: bool,
    total_frames: i32,
    pieces_placed: u32,
    lines_cleared: u32,
    final_time_ms: i64,
    inputs: i64,
    allow_180: bool,
    nolockout: bool,
    pro: bool,
    stride: bool,
    levels: bool,
    levelgbase: f64,
    levelgspeed: f64,
    gravity_may20g: bool,
    handling: HandlingFx,
}

#[derive(Deserialize)]
struct MetaFixture {
    cases: Vec<MetaCase>,
}

#[test]
fn parser_matches_fixture_metadata() {
    let raw = std::fs::read_to_string("tests/fixtures/replay_meta.json").unwrap();
    let fixture: MetaFixture = serde_json::from_str(&raw).unwrap();
    assert_eq!(fixture.cases.len(), 4);
    for case in fixture.cases {
        let path = format!("tests/data/{}", case.rel);
        let r = load_recording(Path::new(&path)).unwrap();
        assert_eq!(r.replay_id, case.replay_id, "replay_id {}", case.rel);
        assert_eq!(r.gamemode, case.gamemode, "gamemode {}", case.rel);
        assert_eq!(r.seed, case.seed, "seed {}", case.rel);
        assert_eq!(r.no_szo, case.no_szo, "no_szo {}", case.rel);
        assert_eq!(r.total_frames, case.total_frames, "frames {}", case.rel);
        assert_eq!(r.pieces_placed, case.pieces_placed, "pieces {}", case.rel);
        assert_eq!(r.lines_cleared, case.lines_cleared, "lines {}", case.rel);
        assert_eq!(r.final_time_ms, case.final_time_ms, "time {}", case.rel);
        assert_eq!(r.inputs, case.inputs, "inputs {}", case.rel);
        assert_eq!(r.allow_180, case.allow_180, "allow180 {}", case.rel);
        assert_eq!(r.nolockout, case.nolockout, "nolockout {}", case.rel);
        assert_eq!(r.pro, case.pro, "pro {}", case.rel);
        assert_eq!(r.stride, case.stride, "stride {}", case.rel);
        assert_eq!(r.levels, case.levels, "levels {}", case.rel);
        assert_eq!(r.levelgbase, case.levelgbase, "levelgbase {}", case.rel);
        assert_eq!(r.levelgspeed, case.levelgspeed, "levelgspeed {}", case.rel);
        assert_eq!(r.gravity_may20g, case.gravity_may20g, "may20g {}", case.rel);
        assert_eq!(r.handling.arr, case.handling.arr, "arr {}", case.rel);
        assert_eq!(r.handling.das, case.handling.das, "das {}", case.rel);
        assert_eq!(r.handling.dcd, case.handling.dcd, "dcd {}", case.rel);
        assert_eq!(r.handling.sdf, case.handling.sdf, "sdf {}", case.rel);
        assert_eq!(
            r.handling.safelock, case.handling.safelock,
            "safelock {}",
            case.rel
        );
        assert_eq!(
            r.handling.cancel, case.handling.cancel,
            "cancel {}",
            case.rel
        );
        assert_eq!(
            r.handling.may20g, case.handling.may20g,
            "h.may20g {}",
            case.rel
        );
        assert_eq!(r.handling.irs, case.handling.irs, "irs {}", case.rel);
        assert_eq!(r.handling.ihs, case.handling.ihs, "ihs {}", case.rel);
        assert!(!r.key_events.is_empty(), "key_events {}", case.rel);
        assert!(matches!(
            r.key_events[0].kind,
            KeyKind::KeyDown | KeyKind::KeyUp
        ));
    }
}
