use chess_engine::{
    position::Position,
    types::{Piece, Side, Square},
};

#[test]
fn test_load_starting_position_from_fen() {
    // Load the standard starting position
    let position = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
    assert!(position.is_ok(), "Failed to load starting position FEN");

    let position = position.unwrap();

    // Verify white is to move
    assert_eq!(position.side, Side::White);

    // Verify castling rights (all enabled: 0b1111 = 15)
    assert_eq!(position.castle, 0b1111);

    // Check some piece positions
    assert!(
        position.board.bit_pieces[Side::White as usize][Piece::King as usize]
            .is_bit_set(Square::E1)
    );
    assert!(
        position.board.bit_pieces[Side::Black as usize][Piece::King as usize]
            .is_bit_set(Square::E8)
    );
    assert!(
        position.board.bit_pieces[Side::White as usize][Piece::Pawn as usize]
            .is_bit_set(Square::E2)
    );
    assert!(
        position.board.bit_pieces[Side::Black as usize][Piece::Pawn as usize]
            .is_bit_set(Square::E7)
    );
}

#[test]
fn test_load_custom_position_from_fen() {
    // Load a position with a specific setup (Scandinavian Defense after 1.e4 d5)
    let result =
        Position::from_fen("rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2");
    assert!(result.is_ok(), "Failed to load custom position FEN");

    let position = result.unwrap();

    // Verify white is to move
    assert_eq!(position.side, Side::White);

    // Check that black pawn is on d5
    assert!(
        position.board.bit_pieces[Side::Black as usize][Piece::Pawn as usize]
            .is_bit_set(Square::D5)
    );

    // Check that white pawn is on e4
    assert!(
        position.board.bit_pieces[Side::White as usize][Piece::Pawn as usize]
            .is_bit_set(Square::E4)
    );

    // Check that e2 is empty (pawn moved to e4)
    assert!(
        !position.board.bit_pieces[Side::White as usize][Piece::Pawn as usize]
            .is_bit_set(Square::E2)
    );
}

#[test]
fn test_load_position_with_black_to_move() {
    // Load a position with black to move
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1");
    assert!(result.is_ok(), "Failed to load position with black to move");

    let position = result.unwrap();

    // Verify black is to move
    assert_eq!(position.side, Side::Black);
}

#[test]
fn test_load_position_with_limited_castling() {
    // Position where only white can castle kingside and black can castle queenside
    let result = Position::from_fen("r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w Kq - 0 1");
    assert!(
        result.is_ok(),
        "Failed to load position with limited castling"
    );

    let position = result.unwrap();

    // Verify castling rights (K=1, q=8, so 0b1001 = 9)
    assert_eq!(position.castle, 0b1001);
}

#[test]
fn test_load_position_with_no_castling() {
    // Position with no castling rights
    let result = Position::from_fen("r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w - - 0 1");
    assert!(result.is_ok(), "Failed to load position with no castling");

    let position = result.unwrap();

    // Verify no castling rights
    assert_eq!(position.castle, 0);
}

#[test]
fn test_halfmove_clock_parsing() {
    // Position with halfmove clock = 5
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 5 1");
    assert!(result.is_ok(), "Failed to load FEN with halfmove clock");

    let position = result.unwrap();

    // Verify halfmove clock
    assert_eq!(position.fifty, 5, "Halfmove clock should be 5");
}

#[test]
fn test_fullmove_number_parsing_white_to_move() {
    // Position at move 10, white to move
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 10");
    assert!(result.is_ok(), "Failed to load FEN with fullmove number");

    let position = result.unwrap();

    // Verify ply_from_start_of_game
    // Move 10, white to move = (10-1)*2 + 0 = 18 halfmoves
    assert_eq!(
        position.ply_from_start_of_game, 18,
        "Ply from start should be 18"
    );
}

#[test]
fn test_fullmove_number_parsing_black_to_move() {
    // Position at move 10, black to move
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 10");
    assert!(result.is_ok(), "Failed to load FEN with fullmove number");

    let position = result.unwrap();

    // Verify ply_from_start_of_game
    // Move 10, black to move = (10-1)*2 + 1 = 19 halfmoves
    assert_eq!(
        position.ply_from_start_of_game, 19,
        "Ply from start should be 19"
    );
}

#[test]
fn test_default_values_when_fields_missing() {
    // Minimal FEN (only first 3 fields)
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq");
    assert!(
        result.is_ok(),
        "Failed to load minimal FEN (missing optional fields)"
    );

    let position = result.unwrap();

    // Verify defaults
    assert_eq!(position.fifty, 0, "Halfmove clock should default to 0");
    assert_eq!(
        position.ply_from_start_of_game, 0,
        "Ply should default to 0 for white"
    );
}

#[test]
fn test_en_passant_validation_white() {
    // Valid en passant square for white (rank 6)
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 1");
    assert!(
        result.is_ok(),
        "Should accept valid en passant square on rank 6 for white"
    );

    // Invalid en passant square for white (wrong rank)
    let result2 = Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e3 0 1");
    assert!(
        result2.is_err(),
        "Should reject invalid en passant square on rank 3 for white"
    );
}

#[test]
fn test_en_passant_validation_black() {
    // Valid en passant square for black (rank 3)
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1");
    assert!(
        result.is_ok(),
        "Should accept valid en passant square on rank 3 for black"
    );

    // Invalid en passant square for black (wrong rank)
    let result2 = Position::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e6 0 1");
    assert!(
        result2.is_err(),
        "Should reject invalid en passant square on rank 6 for black"
    );
}

#[test]
fn test_complete_fen_with_all_fields() {
    // Complete FEN with all 6 fields
    let result =
        Position::from_fen("r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq e6 3 3");
    assert!(
        result.is_ok(),
        "Failed to load complete FEN with all fields"
    );

    let position = result.unwrap();

    // Verify all fields
    assert_eq!(position.side, Side::White);
    assert_eq!(position.castle, 0b1111); // KQkq
    assert_eq!(position.fifty, 3);
    assert_eq!(position.ply_from_start_of_game, 4); // (3-1)*2 + 0 = 4

    // Verify some piece positions
    assert!(
        position.board.bit_pieces[Side::Black as usize][Piece::Knight as usize]
            .is_bit_set(Square::C6)
    );
    assert!(
        position.board.bit_pieces[Side::White as usize][Piece::Knight as usize]
            .is_bit_set(Square::F3)
    );
}

#[test]
fn test_invalid_halfmove_clock() {
    // Invalid halfmove clock (not a number)
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - abc 1");
    assert!(
        result.is_err(),
        "Should reject invalid halfmove clock format"
    );
}

#[test]
fn test_invalid_fullmove_number() {
    // Invalid fullmove number (not a number)
    let result = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 xyz");
    assert!(
        result.is_err(),
        "Should reject invalid fullmove number format"
    );

    // Invalid fullmove number (zero)
    let result2 = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 0");
    assert!(
        result2.is_err(),
        "Should reject fullmove number less than 1"
    );
}
