use super::*;

const fn is_promoted_piece(piece: Piece) -> bool {
    matches!(
        piece.piece_type(),
        PieceType::PRO_PAWN
            | PieceType::PRO_LANCE
            | PieceType::PRO_KNIGHT
            | PieceType::PRO_SILVER
            | PieceType::HORSE
            | PieceType::DRAGON
    )
}
use crate::types::{Color, File, Hand, Move, Piece, PieceType, Rank, Square, SQ_51, SQ_55, SQ_59};

/// `PackedPiece` の基本的なエンコード・デコード動作を検証
#[test]
fn test_packed_piece_encoding_roundtrip() {
    assert_eq!(PackedPiece::EMPTY.raw(), 0);
    assert!(PackedPiece::EMPTY.is_empty());

    let black_pawn = PackedPiece::new(PieceType::PAWN, Color::BLACK, false);
    assert_eq!(black_pawn.piece_type(), PieceType::PAWN);
    assert_eq!(black_pawn.color(), Color::BLACK);
    assert!(!black_pawn.is_promoted());

    let white_pro_silver = PackedPiece::new(PieceType::SILVER, Color::WHITE, true);
    assert_eq!(white_pro_silver.piece_type(), PieceType::SILVER);
    assert_eq!(white_pro_silver.color(), Color::WHITE);
    assert!(white_pro_silver.is_promoted());

    let pieces = [
        Piece::B_PAWN,
        Piece::W_LANCE,
        Piece::B_KNIGHT,
        Piece::W_SILVER,
        Piece::B_GOLD,
        Piece::W_BISHOP,
        Piece::B_ROOK,
        Piece::W_KING,
    ];

    for piece in pieces {
        let packed = PackedPiece::from_piece(piece);
        let converted = packed.to_piece();
        assert_eq!(piece, converted);
    }
}

/// `BoardArray` が配列として正常に動作するかを確認
#[test]
fn test_board_array_behaves_like_81_square_buffer() {
    let mut board = BoardArray::empty();
    board.set(SQ_55, Piece::B_KING);

    assert!(!board.get(SQ_55).is_empty());
    assert_eq!(board.get(SQ_55).piece_type(), PieceType::KING);

    let non_empty_count = board.iter().filter(|(_, p)| !p.is_empty()).count();
    assert_eq!(non_empty_count, 1);
}

/// `Position::new` と `from_startpos` の初期状態を検証
#[test]
fn test_position_basic_state_and_startpos() {
    let pos = Position::empty();
    assert_eq!(pos.turn(), Color::BLACK);
    assert_eq!(pos.game_ply(), 1);
    assert_eq!(pos.hand(Color::BLACK), Hand::ZERO);
    assert_eq!(pos.hand(Color::WHITE), Hand::ZERO);

    let start = crate::board::hirate_position();

    assert_eq!(start.piece_on(SQ_59).piece_type(), PieceType::KING);
    assert_eq!(start.piece_on(SQ_59).color(), Color::BLACK);
    assert_eq!(start.piece_on(SQ_51).piece_type(), PieceType::KING);
    assert_eq!(start.piece_on(SQ_51).color(), Color::WHITE);

    assert_eq!(start.turn(), Color::BLACK);
    assert_eq!(start.game_ply(), 1);
}

/// SFEN との往復と最低限の局面検証が成立するかを確認
#[test]
fn test_position_sfen_roundtrip_and_validation() {
    let sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1";

    let pos = crate::board::position_from_sfen(sfen).unwrap();
    let generated = pos.to_sfen(None);
    assert_eq!(sfen, generated);

    let mut invalid = Position::empty();
    assert!(invalid.is_valid());

    invalid.board.set(SQ_59, Piece::B_KING);
    invalid.board.set(SQ_51, Piece::W_KING);
    invalid.rebuild_bitboards();

    assert!(invalid.is_valid());
}

/// `flipped_sfen` が盤面はそのままで先後反転することを確認
#[test]
fn test_position_flipped_sfen_matches_yaneuraou() {
    let sfen = "lnsgk1snl/1r4g2/p1ppppb1p/6pP1/7R1/2P6/P2PPPP1P/1SG6/LN2KGSNL b BP2p 21";
    let expected = "lnsgk2nl/6gs1/p1pppp2p/6p2/1r7/1pP6/P1BPPPP1P/2G4R1/LNS1KGSNL w 2Pbp 21";

    let pos = crate::board::position_from_sfen(sfen).unwrap();
    assert_eq!(pos.to_sfen_flipped(None), expected);
}

#[test]
fn test_position_move_from_csa_and_move() {
    let pos = crate::board::hirate_position();

    let mv = pos.move_from_csa("7776FU");
    assert_eq!(mv.to_usi(), "7g7f");

    let mv = pos.move_from_csa("+7776FU");
    assert_eq!(mv.to_usi(), "7g7f");

    let mv16 = Move::normal("7g".parse().unwrap(), "7f".parse().unwrap());
    let mv = pos.move32_from_move(mv16);
    assert_eq!(mv.to_usi(), "7g7f");

    let mut promo_pos = Position::empty();
    let from = "2b".parse::<Square>().unwrap();
    promo_pos.board.set(from, Piece::B_BISHOP);
    let mv = promo_pos.move_from_csa("2233UM");
    assert_eq!(mv.to_usi(), "2b3c+");
}

/// 通常手・捕獲・打ち・成り・不正手など `apply_move32` 派生の基本挙動を網羅
#[test]
fn test_apply_move_variants() {
    let mut pos = crate::board::hirate_position();

    let from = Square::from_file_rank(File::FILE_7, Rank::RANK_7);
    let to = Square::from_file_rank(File::FILE_7, Rank::RANK_6);
    let piece = pos.piece_on(from);
    let mv = Move32::normal(from, to, piece);

    let initial_ply = pos.game_ply();
    pos.apply_move32(mv);
    assert_eq!(pos.piece_on(from), Piece::NONE);
    assert_eq!(pos.piece_on(to), piece);
    assert_eq!(pos.turn(), Color::WHITE);
    assert_eq!(pos.game_ply(), initial_ply + 1);

    let sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/4p4/4P4/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1";
    let mut capture_pos = crate::board::position_from_sfen(sfen).unwrap();

    let from = Square::from_file_rank(File::FILE_5, Rank::RANK_6);
    let to = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
    let piece = capture_pos.piece_on(from);
    let captured = capture_pos.piece_on(to);
    let mv = Move32::normal(from, to, piece);
    capture_pos.apply_move32(mv);
    let state_stack = capture_pos.state_stack();
    let state = state_stack.current();
    assert_eq!(state.captured(), Some(captured));
    assert_eq!(capture_pos.hand(Color::BLACK).pawn_count(), 1);

    let sfen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPP1PPPP/1B5R1/LNSGKGSNL b P 1";
    let mut drop_pos = crate::board::position_from_sfen(sfen).unwrap();
    let to = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
    let mv = Move32::drop(PieceType::PAWN, to, Color::BLACK);
    let initial_hand = drop_pos.hand(Color::BLACK);
    drop_pos.apply_move32(mv);
    let placed_piece = drop_pos.piece_on(to);
    assert_eq!(placed_piece, Piece::B_PAWN);
    assert_eq!(drop_pos.hand(Color::BLACK).pawn_count(), initial_hand.pawn_count() - 1);
    assert_eq!(drop_pos.state_stack().current().captured, Piece::NONE);

    let sfen = "lnsgkgsnl/1r5b1/2R6/ppppppppp/9/9/PPPPPPPPP/1B7/LNSGKGSNL b - 1";
    let mut promote_pos = crate::board::position_from_sfen(sfen).unwrap();
    let from = Square::from_file_rank(File::FILE_7, Rank::RANK_3);
    let to = Square::from_file_rank(File::FILE_7, Rank::RANK_2);
    let piece = promote_pos.piece_on(from);
    let mv = Move32::promotion(from, to, piece);
    promote_pos.apply_move32(mv);
    assert!(is_promoted_piece(promote_pos.piece_on(to)));

    let invalid_move_pos = crate::board::hirate_position();

    let from = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
    let to = Square::from_file_rank(File::FILE_5, Rank::RANK_4);
    let mv = Move32::normal(from, to, Piece::B_PAWN);
    assert!(!invalid_move_pos.is_legal_move32(mv));

    let to = Square::from_file_rank(File::FILE_5, Rank::RANK_5);
    let mv = Move32::drop(PieceType::PAWN, to, Color::BLACK);
    assert!(!invalid_move_pos.is_legal_move32(mv));
}
