from __future__ import annotations

import chess

# Unicode piece symbols
PIECE_SYMBOLS = {
    (chess.PAWN, chess.WHITE): "\u265f",
    (chess.KNIGHT, chess.WHITE): "\u265e",
    (chess.BISHOP, chess.WHITE): "\u265d",
    (chess.ROOK, chess.WHITE): "\u265c",
    (chess.QUEEN, chess.WHITE): "\u265b",
    (chess.KING, chess.WHITE): "\u265a",
    (chess.PAWN, chess.BLACK): "\u265f",
    (chess.KNIGHT, chess.BLACK): "\u265e",
    (chess.BISHOP, chess.BLACK): "\u265d",
    (chess.ROOK, chess.BLACK): "\u265c",
    (chess.QUEEN, chess.BLACK): "\u265b",
    (chess.KING, chess.BLACK): "\u265a",
}

# ANSI color codes
RESET = "\033[0m"
BOLD = "\033[1m"
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
ORANGE = "\033[38;5;208m"
WHITE_TEXT = "\033[97m"
DIM = "\033[2m"

# Board square background colors
LIGHT_SQ = "\033[48;5;180m"
DARK_SQ = "\033[48;5;95m"
LIGHT_SQ_HIGHLIGHT = "\033[48;5;228m"
DARK_SQ_HIGHLIGHT = "\033[48;5;186m"

# Piece text colors on the board
WHITE_PIECE = "\033[97m"
BLACK_PIECE = "\033[30m"

EMPTY_SQUARE = "\u00b7"


def _square_bg(square: int, highlight_squares: set[int]) -> str:
    is_light = (chess.square_file(square) + chess.square_rank(square)) % 2 == 1
    if square in highlight_squares:
        return LIGHT_SQ_HIGHLIGHT if is_light else DARK_SQ_HIGHLIGHT
    return LIGHT_SQ if is_light else DARK_SQ


def render_board(
    board: chess.Board,
    perspective: chess.Color = chess.WHITE,
    last_move: chess.Move | None = None,
) -> str:
    highlight_squares: set[int] = set()
    if last_move:
        highlight_squares.add(last_move.from_square)
        highlight_squares.add(last_move.to_square)

    ranks = range(7, -1, -1) if perspective == chess.WHITE else range(8)
    files = range(8) if perspective == chess.WHITE else range(7, -1, -1)
    file_labels = [chess.FILE_NAMES[f] for f in files]

    lines: list[str] = []

    # Top file labels
    lines.append("    " + "  ".join(file_labels) + "  ")

    for rank in ranks:
        rank_label = str(rank + 1)
        cells: list[str] = []
        for file in files:
            sq = chess.square(file, rank)
            bg = _square_bg(sq, highlight_squares)
            piece = board.piece_at(sq)
            if piece:
                symbol = PIECE_SYMBOLS[(piece.piece_type, piece.color)]
                piece_color = WHITE_PIECE if piece.color == chess.WHITE else BLACK_PIECE
                cells.append(f"{bg}{piece_color} {symbol} {RESET}")
            else:
                cells.append(f"{bg} {EMPTY_SQUARE} {RESET}")
        lines.append(f" {rank_label} {''.join(cells)} {rank_label}")

    # Bottom file labels
    lines.append("    " + "  ".join(file_labels) + "  ")

    return "\n".join(lines)


def render_eval_bar(
    eval_score: float,
    is_mate: bool,
    mate_in: int | None = None,
    width: int = 30,
) -> str:
    if is_mate and mate_in is not None:
        if mate_in > 0:
            fill = width
            label = f"#{mate_in}"
            color = GREEN
        else:
            fill = 0
            label = f"#-{abs(mate_in)}"
            color = RED
    else:
        clamped = max(-10.0, min(10.0, eval_score))
        fraction = (clamped + 10.0) / 20.0
        fill = int(fraction * width)
        if eval_score > 0.3:
            color = GREEN
        elif eval_score < -0.3:
            color = RED
        else:
            color = WHITE_TEXT
        sign = "+" if eval_score >= 0 else ""
        label = f"{sign}{eval_score:.1f}"

    bar = "\u25b0" * fill + "\u25b1" * (width - fill)
    return f"  {color}{bar}  {label}{RESET}"


def render_game_display(
    board: chess.Board,
    perspective: chess.Color,
    last_move: chess.Move | None,
    eval_score: float | None,
    is_mate: bool,
    mate_in: int | None,
    player_name: str,
    opponent_name: str,
    move_list: str,
    difficulty_label: str = "",
) -> str:
    sections: list[str] = []

    # Opponent label (top)
    opp_piece = "\u265f" if perspective == chess.WHITE else "\u2659"
    opp_line = f"  {opp_piece} {opponent_name}"
    if difficulty_label:
        opp_line += f"  {DIM}({difficulty_label}){RESET}"
    sections.append(opp_line)

    # Eval display next to opponent
    if eval_score is not None:
        eval_display = eval_score if perspective == chess.WHITE else -eval_score
        if is_mate and mate_in is not None:
            mate_display = mate_in if perspective == chess.WHITE else -mate_in
            eval_label = f"#{mate_display}"
        else:
            sign = "+" if eval_display >= 0 else ""
            eval_label = f"{sign}{eval_display:.1f}"
        sections[-1] += f"          Eval: {eval_label}"

    sections.append("")

    # Board
    sections.append(render_board(board, perspective, last_move))

    sections.append("")

    # Player label (bottom)
    player_piece = "\u2659" if perspective == chess.WHITE else "\u265f"
    sections.append(f"  {player_piece} {player_name}")

    # Eval bar
    if eval_score is not None:
        display_eval = eval_score if perspective == chess.WHITE else -eval_score
        display_mate = None
        if is_mate and mate_in is not None:
            display_mate = mate_in if perspective == chess.WHITE else -mate_in
        sections.append("")
        sections.append(render_eval_bar(display_eval, is_mate, display_mate))

    # Move list
    if move_list:
        sections.append("")
        sections.append(f"  {DIM}Moves: {move_list}{RESET}")

    return "\n".join(sections)
