import pytest
from brownie import Chess, accounts, web3, reverts

@pytest.fixture
def chess_contract():
    return accounts[0].deploy(Chess)


@pytest.mark.parametrize("game_state,movement,blackMove,opponentExtra,expected_new_state,expected_new_player_extra", 
    [
        # White move 1 up
        ("0x0000000000000000000000000000000000000000000000000000000100000000", "0x210", False, "0x00",
         "0x0000000000000000000000000000000000000000000000010000000000000000", "0x00"),
        # White move 1 up, right side
        ("0x0000000000000000000000000000000000000000000000001000000000000000", "0x3d7", False, "0x00",
         "0x0000000000000000000000000000000000000000100000000000000000000000", "0x00"),
        # White move 2 up
        ("0x0000000000000000000000000000000000000000000000000000100000000000", "0x2db", False, "0x00",
         "0x0000000000000000000000000000000000001000000000000000000000000000", "0x13"),
        # Black move 1 down
        ("0x0000000000009000000000000000000000000000000000000000000000000000", "0xceb", True, "0x00",
         "0x0000000000000000000090000000000000000000000000000000000000000000", "0x00"),
        # Black move 1 down, right side
        ("0x0000000090000000000000000000000000000000000000000000000000000000", "0xdef", True, "0x00",
         "0x0000000000000000900000000000000000000000000000000000000000000000", "0x00"),
        # Black move 2 down
        ("0x0000000000009000000000000000000000000000000000000000000000000000", "0xce3", True, "0x00",
         "0x0000000000000000000000000000900000000000000000000000000000000000", "0x2b"),
        # White take black right
        ("0x0000000000000000000000000009000000001000000000000000000000000000", "0x6e4", False, "0x00",
         "0x0000000000000000000000000001000000000000000000000000000000000000", "0x00"),
        # White take black left
        ("0x0000000000000000000000000000090000001000000000000000000000000000", "0x6e2", False, "0x00",
         "0x0000000000000000000000000000010000000000000000000000000000000000", "0x00"),
        # Black take white right
        ("0x0000000000000000000000000000900000010000000000000000000000000000", "0x8dc", True, "0x00",
         "0x0000000000000000000000000000000000090000000000000000000000000000", "0x00"),
        # Black take white left
        ("0x0000000000000000000000000000900000000100000000000000000000000000", "0x8da", True, "0x00",
         "0x0000000000000000000000000000000000000900000000000000000000000000", "0x00"),
        # White pawn take en passant black
        ("0x0000000000000000000000000001900000000000000000000000000000000000", "0x92b", False, "0x2b",
         "0x0000000000000000000010000000000000000000000000000000000000000000", "0x00"),
        # Black pawn take en passant white
        ("0x0000000000000000000000000000000000091000000000000000000000000000", "0x713", True, "0x13",
         "0x0000000000000000000000000000000000000000000090000000000000000000", "0x00"),
        # White queen take en passant black
        ("0x0000000000000000500000000000009000000000000000000000000000000000", "0xbe9", False, "0x29",
         "0x0000000000000000000000500000000000000000000000000000000000000000", "0x00"),
        # Black bishop take en passant white
        ("0x0a00000000000000000000000000000000000010000000000000000000000000", "0xf91", True, "0x11",
         "0x0000000000000000000000000000000000000000000000a00000000000000000", "0x00"),
        # White move 1 up promotion to Queen
        ("0x0000000001000000000000000000000000000000000000000000000000000000", "0x5dbe", False, "0x00",
         "0x0500000000000000000000000000000000000000000000000000000000000000", "0x00"),
        # White move 1 up promotion to Bishop
        ("0x0000000000000100000000000000000000000000000000000000000000000000", "0x2cba", False, "0x00",
         "0x0000020000000000000000000000000000000000000000000000000000000000", "0x00"),
        # White move 1 up promotion to Rook
        ("0x0000000000000001000000000000000000000000000000000000000000000000", "0x4c38", False, "0x00",
         "0x0000000400000000000000000000000000000000000000000000000000000000", "0x00"),
        # White move 1 up promotion to Knight
        ("0x0000000010000000000000000000000000000000000000000000000000000000", "0x3dff", False, "0x00",
         "0x3000000000000000000000000000000000000000000000000000000000000000", "0x00"),

        # Black move 1 down promotion to Queen
        ("0x0000000000000000000000000000000000000000000000000009000000000000", "0x5304", True, "0x00",
         "0x00000000000000000000000000000000000000000000000000000000000d0000", "0x00"),
        # Black move 1 down promotion to Bishop
        ("0x0000000000000000000000000000000000000000000000000000000900000000", "0x2200", True, "0x00",
         "0x000000000000000000000000000000000000000000000000000000000000000a", "0x00"),
        # Black move 1 down promotion to Rook
        ("0x0000000000000000000000000000000000000000000000009000000000000000", "0x43c7", True, "0x00",
         "0x00000000000000000000000000000000000000000000000000000000c0000000", "0x00"),
        # Black move 1 down promotion to Knight
        ("0x0000000000000000000000000000000000000000000000000000090000000000", "0x3282", True, "0x00",
         "0x0000000000000000000000000000000000000000000000000000000000000b00", "0x00"),
    ])
def test_valid_movements(chess_contract, game_state, movement, blackMove, opponentExtra, expected_new_state, expected_new_player_extra):
    result = chess_contract.verifyExecuteMove(game_state, movement, "0x00", opponentExtra, blackMove)
    assert result[0] == expected_new_state
    assert result[1] == expected_new_player_extra
    assert result[2] == "0xff"


@pytest.mark.parametrize("game_state,movement,blackMove,err", 
    [
        # No move
        ("0x0000000000000000000000000000000000000000000000000000000100000000", "0x208", False, "inv move stale"),
        # Single move sideways white
        ("0x0000000000000000000000000000000000000000000000000000000100000000", "0x209", False, "inv move"),
        # Single move sideways white, left
        ("0x0000000000000000000000000000000000000000000000000000001000000000", "0x248", False, "inv move"),
        # White single move down
        ("0x0000000000000000000000000000000000000000000000000000100000000000", "0x2c3", False, "inv move"),
        # Black single move up
        ("0x0000000000009000000000000000000000000000000000000000000000000000", "0xcfb", True, "inv move"),
        # White invalid 2 up
        ("0x0000000000000000000000000000000000000000000010000000000000000000", "0x4e3", False, "inv move"),
        # Black invalid 2 down
        ("0x0000000000000000000090000000000000000000000000000000000000000000", "0xadb", True, "inv move"),
        # White 1 up non-empty square (same color)
        ("0x0000000000000000000000000000000000000000000010000000100000000000", "0x2d3", False, "inv move"),
        # White 2 up non-empty square (same color)
        ("0x0000000000000000000000000000000000001000000000000000100000000000", "0x2db", False, "inv move"),
        # White 2 up non-empty square in between (same color)
        ("0x0000000000000000000000000000000000000000000010000000100000000000", "0x2db", False, "inv move"),

        # Black 1 down non-empty square (same color)
        ("0x0000000000090000000900000000000000000000000000000000000000000000", "0xd2c", True, "inv move"),
        # Black 2 down non-empty square (same color)
        ("0x0000000000090000000000000009000000000000000000000000000000000000", "0xd24", True, "inv move"),
        # Black 2 down non-empty square in between (same color)
        ("0x0000000000090000000900000000000000000000000000000000000000000000", "0xd24", True, "inv move"),

        # White take empty square right
        ("0x0000000000000000000000000000000000001000000000000000000000000000", "0x6e4", False, "inv move"),
        # White take same color
        ("0x0000000000000000000000000001000000001000000000000000000000000000", "0x6e4", False, "inv move"),

        # Black take empty square right
        ("0x0000000000000000000000000000900000000000000000000000000000000000", "0x8dc", True, "inv move"),
        # Black take same color
        ("0x0000000000000000000000000000900000090000000000000000000000000000", "0x8dc", True, "inv move"),

        # White take wrap-around piece left
        ("0x0000000000000000000000000000000000000000000000009000000100000000", "0x20f", False, "inv move"),
        # White take wrap-around piece right
        ("0x0000000000000000000000000000000000000009000000001000000000000000", "0x3d8", False, "inv move"),

        # Black take wrap-around piece left
        ("0x0000000000000009000000001000000000000000000000000000000000000000", "0xc27", True, "inv move"),
        # Black take wrap-around piece right
        ("0x0000000090000001000000000000000000000000000000000000000000000000", "0xdf0", True, "inv move"),
    ])
def test_invalid_movements(chess_contract, game_state, movement, blackMove, err):
    with reverts(err):
        chess_contract.verifyExecuteMove(game_state, movement, "0x00", "0x00", blackMove)


@pytest.mark.parametrize("start_game_state,start_player_state,start_opponent_state,movements,expected_new_state", 
    [
        # En passant white pawn
        ("0x000e000000000000000000a00000000000000000000000000001000000060000", "0x000704ff", "0x383f3cff", ["0x31c", "0xa54"],
         "0x000e000000000000000000000000000000000000000a00000000000000060000"),
        # En passant black pawn
        ("0x000e000000090000000000000000000000000000000000000000020000060000", "0x000704ff", "0x383f3cff", ["0x291", "0xd24", "0x46c"],
         "0x000e000000000000000200000000000000000000000000000000000000060000"),
        # En passant white pawn, taken by black pawn
        ("0x000e000000000000000000000000000009000000000000000010000000060000", "0x000704ff", "0x383f3cff", ["0x35d", "0x795"],
         "0x000e000000000000000000000000000000000000009000000000000000060000"),
        # En passant black pawn, taken by white pawn
        ("0x000e000000090000000000000000000000100000000000000000000000060000", "0x000704ff", "0x383f3cff", ["0x765", "0xd24", "0x96c"],
         "0x000e000000000000000100000000000000000000000000000000000000060000"),
    ])
def test_valid_movement_combinations(chess_contract, start_game_state, start_player_state, start_opponent_state, movements, expected_new_state):
    result = chess_contract.checkGame(start_game_state, start_player_state, start_opponent_state, False, movements)
    assert result[0] == 0
    assert result[1] == expected_new_state