use super::*;
use crate::board::test_support::move_from_usi_expect;
use crate::types::{Color, Hand, Piece, PieceType, MOVE_NONE};

#[allow(clippy::cognitive_complexity)]
fn assert_state_info_matches(actual: &StateInfo, expected: &StateInfo) {
    assert_eq!(actual.board_key, expected.board_key);
    assert_eq!(actual.hand_key, expected.hand_key);
    assert_eq!(actual.checkers, expected.checkers);
    assert_eq!(actual.pinners, expected.pinners);
    assert_eq!(actual.blockers_for_king, expected.blockers_for_king);
    assert_eq!(actual.check_squares, expected.check_squares);
    assert_eq!(actual.continuous_check, expected.continuous_check);
    assert_eq!(actual.repetition_counter, expected.repetition_counter);
    assert_eq!(actual.repetition_distance, expected.repetition_distance);
    assert_eq!(actual.repetition_times, expected.repetition_times);
    assert_eq!(actual.plies_from_null, expected.plies_from_null);
    assert_eq!(actual.hand, expected.hand);
    assert_eq!(actual.repetition_type, expected.repetition_type);
    assert_eq!(actual.last_move, expected.last_move);
    assert_eq!(actual.last_moved_piece_type, expected.last_moved_piece_type);
}

#[test]
#[allow(clippy::cognitive_complexity)]
fn test_state_info_default() {
    let info = StateInfo::default();
    assert_eq!(info.captured, Piece::NONE);
    assert_eq!(info.board_key, ZobristKey::default());
    assert_eq!(info.hand_key, ZobristKey::default());
    assert_eq!(info.repetition_counter, 0);
    assert_eq!(info.repetition_distance, 0);
    assert_eq!(info.repetition_times, 0);
    assert_eq!(info.plies_from_null, 0);
    assert_eq!(info.checkers, Bitboard::EMPTY);
    assert_eq!(info.pinners, [Bitboard::EMPTY; Color::COUNT]);
    assert_eq!(info.blockers_for_king, [Bitboard::EMPTY; Color::COUNT]);
    assert_eq!(info.check_squares, [Bitboard::EMPTY; PieceType::COUNT]);
    // 連続王手カウンタのデフォルト値確認
    assert_eq!(info.continuous_check, [0u16, 0u16]);
    assert_eq!(info.hand, Hand::ZERO);
    // 千日手状態のデフォルト値確認
    assert_eq!(info.repetition_type, RepetitionState::None);
    assert_eq!(info.last_move, MOVE_NONE);
    assert_eq!(info.last_moved_piece_type, PieceType::NONE);
}

#[test]
fn test_state_stack_new() {
    let stack = StateStack::new();
    assert_eq!(stack.depth(), 0);
}

#[test]
fn test_state_stack_push_pop() {
    let mut stack = StateStack::new();

    // 初期状態
    assert_eq!(stack.depth(), 0);

    // プッシュ
    let idx1 = stack.push_empty();
    assert_eq!(idx1, 1);
    assert_eq!(stack.depth(), 1);

    // さらにプッシュ
    let idx2 = stack.push_empty();
    assert_eq!(idx2, 2);
    assert_eq!(stack.depth(), 2);

    // ポップ
    let popped = stack.pop();
    assert_eq!(popped, Some(2));
    assert_eq!(stack.depth(), 1);

    // もう一度ポップ
    let popped = stack.pop();
    assert_eq!(popped, Some(1));
    assert_eq!(stack.depth(), 0);

    // 空の状態でポップ
    let popped = stack.pop();
    assert_eq!(popped, None);
}

#[test]
fn test_state_stack_reset_sfen() {
    let mut stack = StateStack::new();

    // いくつかプッシュ
    stack.push_empty();
    stack.push_empty();
    stack.push_empty();
    assert_eq!(stack.depth(), 3);

    // リセット
    stack.reset_sfen();
    assert_eq!(stack.depth(), 0);
}

#[test]
fn test_state_stack_reset_with_position_matches_init_stack() {
    let mut stack_from_reset = StateStack::new();
    let mut pos = crate::board::hirate_position();

    pos.init_stack();
    stack_from_reset.reset_with_position(&mut pos);

    let init_state = pos.state_stack().current().clone();
    let reset_state = stack_from_reset.current();
    assert_state_info_matches(reset_state, &init_state);
}

#[test]
fn test_state_stack_apply_and_undo_restore_state() {
    let mut pos = crate::board::hirate_position();
    pos.init_stack();

    let initial = pos.state_stack().current().clone();
    let mv = move_from_usi_expect(&pos, "7g7f");
    pos.apply_move32(mv);
    pos.undo_move32(mv).expect("undo move");

    let stack = pos.state_stack();
    let restored = stack.current();
    assert_state_info_matches(restored, &initial);
}

#[test]
fn test_state_info_records_captured_piece() {
    let sfen = "ln1g3nl/1r3kg2/p2pppsp1/3s2p1p/1pp4P1/P1P1SP2P/1PSPP1P2/2GK3R1/LN3G1NL b Bb 1";
    let mut pos = crate::board::position_from_sfen(sfen).expect("valid sfen");
    pos.init_stack();

    let mv = move_from_usi_expect(&pos, "7f7e");
    pos.apply_move32(mv);

    let captured = pos.state_stack().current().captured().expect("captured piece");
    assert_eq!(captured.color(), Color::WHITE);
}

#[test]
fn test_state_stack_get_mut() {
    let mut stack = StateStack::new();

    // プッシュして値を設定
    let idx = stack.push_empty();
    {
        let entry = &mut stack[idx];
        entry.repetition_counter = 3;
        entry.plies_from_null = 10;
    }

    // 取得して確認
    let entry = &stack[idx];
    assert_eq!(entry.repetition_counter, 3);
    assert_eq!(entry.plies_from_null, 10);
}

#[test]
fn test_state_stack_reuse_memory() {
    let mut stack = StateStack::new();

    // 深くプッシュ
    for _ in 0..10 {
        stack.push_empty();
    }

    // 特定の値を設定
    stack.current_mut().repetition_counter = 42;

    // リセット
    stack.reset_sfen();

    // 再度プッシュ（メモリは再利用される）
    for _ in 0..10 {
        stack.push_empty();
    }

    // 前の値が残っている可能性があるが、それは仕様
    // （reset時に全クリアしないため）
    // 重要なのは深さ管理が正しいこと
    assert_eq!(stack.depth(), 10);
}

#[test]
fn test_state_stack_can_grow() {
    let mut stack = StateStack::new();

    for _ in 0..512 {
        stack.push_empty();
    }

    assert_eq!(stack.depth(), 512);
}

#[test]
fn test_continuous_check_counter() {
    let mut info = StateInfo::default();

    // 初期値は両者とも0
    assert_eq!(info.continuous_check[Color::BLACK.to_index()], 0u16);
    assert_eq!(info.continuous_check[Color::WHITE.to_index()], 0u16);

    // 連続王手カウンタを更新
    info.continuous_check[Color::BLACK.to_index()] = 2u16;
    assert_eq!(info.continuous_check[Color::BLACK.to_index()], 2u16);
    assert_eq!(info.continuous_check[Color::WHITE.to_index()], 0u16);

    // リセット
    info.reset_sfen();
    assert_eq!(info.continuous_check[Color::BLACK.to_index()], 0u16);
    assert_eq!(info.continuous_check[Color::WHITE.to_index()], 0u16);
}

#[test]
fn test_repetition_type_field() {
    let mut info = StateInfo::default();

    // 初期値はNone
    assert_eq!(info.repetition_type, RepetitionState::None);

    // 千日手状態を更新
    info.repetition_type = RepetitionState::Win;
    assert_eq!(info.repetition_type, RepetitionState::Win);

    info.repetition_type = RepetitionState::Draw;
    assert_eq!(info.repetition_type, RepetitionState::Draw);

    // リセット
    info.reset_sfen();
    assert_eq!(info.repetition_type, RepetitionState::None);
}
