"""
Practice Projects: Chess Dictionary Validator
Page 127
"""

import sys

tests = [
    # Valid
    {
        "1h": "bking",
        "6c": "wqueen",
        "2g": "bbishop",
        "5h": "bqueen",
        "3e": "wking",
    },
    # Invalid: Illegal total amount of pieces
    {
        "1a": "wrook",
        "1b": "wknight",
        "1c": "wbishop",
        "1d": "wqueen",
        "1e": "wking",
        "1f": "wbishop",
        "1g": "wknight",
        "1h": "wrook",
        "2a": "wpawn",
        "2b": "wpawn",
        "2c": "wpawn",
        "2d": "wpawn",
        "2e": "wpawn",
        "2f": "wpawn",
        "2g": "wpawn",
        "2h": "wpawn",
        "3a": "wrook",
    },
    # Invalid: Illegal amount of pieces
    {
        "8h": "wking",
        "7h": "wking",
    },
    # Invalid: Illegal position length
    {
        "9": "bking",
    },
    # Invalid: Illegal position length
    {
        "9hh": "bking",
    },
    # Invalid: Illegal tile number
    {
        "9h": "bking",
    },
    # Invalid: Illegal tile letter
    {
        "1r": "bking",
    },
    # Invalid: Illegal player color
    {
        "8h": "cking",
    },
    # Invalid: Illegal piece name
    {
        "8h": "wring",
    },
    # Invalid: Illegal piece data type
    {
        "8h": None,
    },
    # Invalid: Illegal board data type
    None,
]


def invalidAmount(name, present, expected):
    err = f"AmountError: total {name}"
    return f"{err}; total == {present}, expected: <= {expected}"


def invalidPos(name, pos, present, expected):
    err = f"PositionError: {name} '{pos}'"
    return f"{err}; length == {present}, expected: {expected}"


def invalidPiece(name, color_piece, present, expected):
    err = f"PieceError: {name} '{color_piece}'"
    return f"{err}; {name} == {present}, expected: {expected}"


def invalidType(name, present, expected):
    err = f"TypeError: {name} type"
    return False, f"{err}; type == {present}, expected: {expected}"


def isValidPos(pos):
    if len(pos) != 2:
        return invalidPos("length", pos, len(pos), 2)
    number, letter = pos[0], pos[1]
    if number not in "12345678":
        return invalidPos("number", pos, number, "1-8")
    if letter not in "abcdefgh":
        return invalidPos("letter", pos, letter, "a-h")


def isValidPiece(color_piece):
    pieces = "pawn knight bishop rook queen king".split()
    color, piece = color_piece[0], color_piece[1:]
    if color not in "bw":
        return invalidPiece("color", color_piece, color, "w | b")
    if piece not in pieces:
        return invalidPiece("piece", color_piece, piece, " | ".join(pieces))


def isValidAmount(player):
    totals = {
        "all": 16,
        "pawn": 8,
        "king": 1,
    }
    total = 0
    for key, val in totals.items():
        if key in player.keys() and player[key] > val:
            return invalidAmount(f"'{key}'", player[key], val)
    for val in player.values():
        total += val
        if total > totals["all"]:
            return invalidAmount("pieces", total, totals["all"])


def isValidChessBoard(board):
    players, valid, msg = {}, True, "Valid"
    if not isinstance(board, dict):
        return invalidType("board", type(board), type({}))
    for pos, piece in board.items():
        if not isinstance(piece, str):
            return invalidType("piece", type(piece), type(""))
        players.setdefault(piece[0], {})
        player = players[piece[0]]
        player.setdefault(piece[1:], 0)
        player[piece[1:]] += 1
        checks = [isValidPos(pos), isValidPiece(piece), isValidAmount(player)]
        for check in checks:
            if check is not None:
                valid, msg = False, check
                break
    return valid, msg


valid = True
for test in tests:
    print(f"Testing board '{test}': ")
    valid, msg = isValidChessBoard(test)
    print("=>", msg)
if not valid:
    sys.exit(1)
