use std::path::Path;

use serde::Deserialize;
use rsstacker::engine::board::Board;
use rsstacker::replay::load_recording;
use rsstacker::score_attack::score_board_locks;
use rsstacker::sim::ReplaySimulator;

#[derive(Deserialize)]
struct ScoreAttackCase {
    rel: String,
    final_score: i64,
    per_lock_scores: Vec<i64>,
}

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

fn per_lock_boards(replay: &rsstacker::replay::Replay) -> Vec<Board> {
    let mut sim = ReplaySimulator::new(replay, 300);
    let mut boards = Vec::new();
    let mut seen = 0u32;
    while sim.step().is_some() {
        if sim.board().pieces_placed != seen {
            seen = sim.board().pieces_placed;
            boards.push(sim.board().clone());
        }
    }
    boards
}

#[test]
fn score_attack_matches_fixture_scores() {
    let raw = std::fs::read_to_string("tests/fixtures/score_attack_scores.json").unwrap();
    let fixture: ScoreAttackFixture = serde_json::from_str(&raw).unwrap();
    assert_eq!(fixture.cases.len(), 2);
    for case in fixture.cases {
        let replay = load_recording(Path::new(&format!("tests/data/{}", case.rel))).unwrap();
        let boards = per_lock_boards(&replay);
        assert_eq!(
            boards.len(),
            case.per_lock_scores.len(),
            "lock count {}",
            case.rel
        );
        for (i, board) in boards.iter().enumerate() {
            let got = score_board_locks(board).score;
            assert_eq!(
                got, case.per_lock_scores[i],
                "score at lock {i} in {}",
                case.rel
            );
        }
        let final_score = score_board_locks(boards.last().unwrap()).score;
        assert_eq!(final_score, case.final_score, "final score {}", case.rel);
    }
}
