//! Unit tests for the replay iterator module.

use cardgame::actions::Action;
use cardgame::engine::GameEngine;
use cardgame::replay::{
    validate_replay, GameReplay, PlayerConfig, ReplayBuilder, ReplayError, ReplayIterator,
    replay_to_end,
};
use cardgame::state::GameMode;
use cardgame::types::CardId;

use super::common::{load_real_card_db, load_real_deck_registry};

/// Create a short replay by playing a real game for a few turns.
fn create_test_replay() -> (GameReplay, cardgame::cards::CardDatabase) {
    let card_db = load_real_card_db();
    let registry = load_real_deck_registry();
    let decks: Vec<_> = registry.decks().collect();
    let deck = &decks[0];

    let mut engine = GameEngine::new(&card_db);
    engine.start_game(deck, deck, 42).unwrap();

    let deck1_ids: Vec<CardId> = deck.cards.iter().map(|&id| CardId(id)).collect();
    let deck2_ids = deck1_ids.clone();

    let mut builder = ReplayBuilder::new(
        42,
        GameMode::default(),
        PlayerConfig {
            name: "P1".to_string(),
            player_type: "test".to_string(),
            deck: deck1_ids,
            deck_name: Some(deck.id.clone()),
            commander: Some(CardId(deck.commander)),
        },
        PlayerConfig {
            name: "P2".to_string(),
            player_type: "test".to_string(),
            deck: deck2_ids,
            deck_name: Some(deck.id.clone()),
            commander: Some(CardId(deck.commander)),
        },
    );

    // Play a few actions (end turns to be safe)
    let mut turn = 1;
    let mut actions_recorded = 0;
    while actions_recorded < 20 && !engine.is_game_over() {
        let legal = engine.get_legal_actions();
        if legal.is_empty() {
            break;
        }
        let action = legal[0]; // Pick first legal action
        builder.record_action(turn, action, None);
        engine.apply_action(action).unwrap();
        actions_recorded += 1;
        if matches!(action, Action::EndTurn) {
            turn += 1;
        }
    }

    let final_life = [
        engine.state.players[0].life as i16,
        engine.state.players[1].life as i16,
    ];
    let result = engine.state.result;
    let replay = builder.finalize(result, turn, final_life);

    (replay, card_db)
}

// ── basic iteration ──────────────────────────────────────────────────────────

#[test]
fn test_new_iterator_starts_at_zero() {
    let (replay, card_db) = create_test_replay();
    let iter = ReplayIterator::new(&replay, &card_db);

    assert_eq!(iter.current_index(), 0);
    assert!(!iter.is_at_end());
    assert!(iter.current_state().is_none()); // Not initialized yet
}

#[test]
fn test_step_returns_actions_in_sequence() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    let step1 = iter.step().unwrap().unwrap();
    assert_eq!(step1.action_index, 0);
    assert_eq!(step1.action, replay.actions[0].action);

    let step2 = iter.step().unwrap().unwrap();
    assert_eq!(step2.action_index, 1);
    assert_eq!(step2.action, replay.actions[1].action);
}

#[test]
fn test_step_returns_none_at_end() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    // Step through all actions
    while iter.step().unwrap().is_some() {}

    assert!(iter.is_at_end());
    assert!(iter.step().unwrap().is_none());
}

#[test]
fn test_step_sets_is_final_on_last_action() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    let mut last_step = None;
    while let Some(step) = iter.step().unwrap() {
        last_step = Some(step);
    }

    assert!(last_step.unwrap().is_final);
}

#[test]
fn test_total_actions() {
    let (replay, card_db) = create_test_replay();
    let iter = ReplayIterator::new(&replay, &card_db);

    assert_eq!(iter.total_actions(), replay.actions.len());
}

// ── step_n ───────────────────────────────────────────────────────────────────

#[test]
fn test_step_n_returns_n_steps() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    let steps = iter.step_n(3).unwrap();
    assert_eq!(steps.len(), 3);
    assert_eq!(iter.current_index(), 3);
}

#[test]
fn test_step_n_stops_at_end() {
    let (replay, card_db) = create_test_replay();
    let total = replay.actions.len();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    let steps = iter.step_n(total + 100).unwrap();
    assert_eq!(steps.len(), total);
    assert!(iter.is_at_end());
}

// ── seek ─────────────────────────────────────────────────────────────────────

#[test]
fn test_seek_forward() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    iter.seek(5).unwrap();
    assert_eq!(iter.current_index(), 5);
    assert!(iter.current_state().is_some());
}

#[test]
fn test_seek_backward_resets_and_replays() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    iter.seek(10).unwrap();
    let state_at_10 = iter.current_state().unwrap().clone();

    iter.seek(5).unwrap();
    assert_eq!(iter.current_index(), 5);

    // Seek forward again to 10 — should produce same state
    iter.seek(10).unwrap();
    let state_at_10_again = iter.current_state().unwrap();
    assert_eq!(state_at_10.current_turn, state_at_10_again.current_turn);
    assert_eq!(
        state_at_10.players[0].life,
        state_at_10_again.players[0].life
    );
}

#[test]
fn test_seek_out_of_bounds() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    let result = iter.seek(replay.actions.len() + 10);
    assert!(matches!(result, Err(ReplayError::SeekOutOfBounds(_))));
}

#[test]
fn test_seek_to_zero() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    iter.step_n(5).unwrap();
    iter.seek(0).unwrap();
    assert_eq!(iter.current_index(), 0);
}

// ── reset ────────────────────────────────────────────────────────────────────

#[test]
fn test_reset_clears_state() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    iter.step_n(5).unwrap();
    assert!(iter.current_state().is_some());

    iter.reset();
    assert_eq!(iter.current_index(), 0);
    assert!(iter.current_state().is_none());
    assert!(!iter.is_at_end());
}

// ── state integrity ──────────────────────────────────────────────────────────

#[test]
fn test_current_state_populated_after_step() {
    let (replay, card_db) = create_test_replay();
    let mut iter = ReplayIterator::new(&replay, &card_db);

    iter.step().unwrap();
    let state = iter.current_state().unwrap();
    assert!(state.players[0].life > 0);
    assert!(state.players[1].life > 0);
}

#[test]
fn test_multiple_iterations_produce_identical_states() {
    let (replay, card_db) = create_test_replay();

    // First iteration
    let mut iter1 = ReplayIterator::new(&replay, &card_db);
    iter1.step_n(10).unwrap();
    let state1 = iter1.current_state().unwrap().clone();

    // Second iteration
    let mut iter2 = ReplayIterator::new(&replay, &card_db);
    iter2.step_n(10).unwrap();
    let state2 = iter2.current_state().unwrap();

    assert_eq!(state1.current_turn, state2.current_turn);
    assert_eq!(state1.players[0].life, state2.players[0].life);
    assert_eq!(state1.players[1].life, state2.players[1].life);
    assert_eq!(
        state1.players[0].creatures.len(),
        state2.players[0].creatures.len()
    );
}

// ── helper functions ─────────────────────────────────────────────────────────

#[test]
fn test_replay_to_end_produces_valid_state() {
    let (replay, card_db) = create_test_replay();

    let final_state = replay_to_end(&replay, &card_db).unwrap();
    // State should have progressed from initial
    assert!(final_state.current_turn >= 1);
}

#[test]
fn test_validate_replay_accepts_valid_replay() {
    let (replay, card_db) = create_test_replay();

    let result = validate_replay(&replay, &card_db);
    assert!(result.is_ok());
}

// ── ReplayBuilder ────────────────────────────────────────────────────────────

#[test]
fn test_replay_builder_creates_valid_replay() {
    let _card_db = load_real_card_db();
    let registry = load_real_deck_registry();
    let decks: Vec<_> = registry.decks().collect();
    let deck = &decks[0];

    let deck_ids: Vec<CardId> = deck.cards.iter().map(|&id| CardId(id)).collect();
    let builder = ReplayBuilder::new(
        99,
        GameMode::default(),
        PlayerConfig {
            name: "Test1".to_string(),
            player_type: "random".to_string(),
            deck: deck_ids.clone(),
            deck_name: None,
            commander: Some(CardId(deck.commander)),
        },
        PlayerConfig {
            name: "Test2".to_string(),
            player_type: "random".to_string(),
            deck: deck_ids,
            deck_name: None,
            commander: Some(CardId(deck.commander)),
        },
    );

    let replay = builder.finalize(None, 0, [30, 30]);
    assert_eq!(replay.header.seed, 99);
    assert_eq!(replay.actions.len(), 0);
    assert!(replay.is_complete()); // finalize with None sets "Draw" result
}

#[test]
fn test_replay_add_action() {
    let deck_ids = vec![CardId(1000); 10];
    let mut replay = GameReplay::new(
        42,
        GameMode::default(),
        PlayerConfig {
            name: "P1".to_string(),
            player_type: "test".to_string(),
            deck: deck_ids.clone(),
            deck_name: None,
            commander: Some(CardId(5000)),
        },
        PlayerConfig {
            name: "P2".to_string(),
            player_type: "test".to_string(),
            deck: deck_ids,
            deck_name: None,
            commander: Some(CardId(5000)),
        },
    );

    replay.add_action(1, Action::EndTurn, None);
    replay.add_action(2, Action::EndTurn, Some(100));

    assert_eq!(replay.action_count(), 2);
    assert_eq!(replay.actions[0].turn, 1);
    assert!(replay.actions[0].thinking_time_us.is_none());
    assert_eq!(replay.actions[1].thinking_time_us, Some(100));
}
