"""Vertical evaluation bar widget for Textual TUI (lichess-style)."""

from __future__ import annotations

from textual.reactive import reactive
from textual.widget import Widget
from rich.style import Style
from rich.text import Text


# Styles for the two halves of the bar
WHITE_STYLE = Style(color="white", bgcolor="white")
BLACK_STYLE = Style(color="rgb(30,30,30)", bgcolor="rgb(30,30,30)")

# Label styles: ensure contrast against the bar section they sit on
LABEL_ON_WHITE_STYLE = Style(color="black", bgcolor="white", bold=True)
LABEL_ON_BLACK_STYLE = Style(color="white", bgcolor="rgb(30,30,30)", bold=True)

# Full block character used for both halves (coloured via style)
BLOCK = "\u2588"

# Eval range clamped to [-MAX_EVAL, MAX_EVAL] for proportional display
MAX_EVAL = 10.0


class EvalBarWidget(Widget):
    """A vertical evaluation bar showing engine eval, similar to lichess."""

    DEFAULT_CSS = """
    EvalBarWidget {
        width: 4;
        height: 100%;
    }
    """

    eval_score: reactive[float] = reactive(0.0)
    is_mate: reactive[bool] = reactive(False)
    mate_in: reactive[int] = reactive(0)
    perspective: reactive[bool] = reactive(True)

    def _format_label(self, display_score: float) -> str:
        """Return the human-readable eval label.

        *display_score* is already oriented so positive = advantage for the
        viewer's side.
        """
        if self.is_mate and self.mate_in != 0:
            # Mate distance, oriented to display perspective
            mate_display = self.mate_in if self.perspective else -self.mate_in
            if mate_display > 0:
                return f"#{mate_display}"
            else:
                return f"#-{abs(mate_display)}"
        sign = "+" if display_score >= 0 else ""
        return f"{sign}{display_score:.1f}"

    def render(self) -> Text:
        """Build the vertical eval bar as a Rich Text object.

        Each row is one cell high and ``self.content_size.width`` characters
        wide, filled with block characters. Rows are joined with newlines
        so that the first row appears at the top.
        """
        height = self.content_size.height
        width = self.content_size.width

        if height <= 0 or width <= 0:
            return Text("")

        # --- compute white-fill proportion ---------------------------------
        if self.is_mate and self.mate_in != 0:
            # Winning side gets the full bar
            white_wins = self.mate_in > 0
            white_fraction = 1.0 if white_wins else 0.0
        else:
            clamped = max(-MAX_EVAL, min(MAX_EVAL, self.eval_score))
            white_fraction = (clamped + MAX_EVAL) / (2 * MAX_EVAL)

        # Perspective: when viewing as Black, invert so *their* advantage
        # fills from the bottom.
        if not self.perspective:
            white_fraction = 1.0 - white_fraction

        # The bar fills from the bottom: bottom rows are "white" (the
        # perspective-positive side).  ``white_rows`` counts how many rows
        # are filled with the white/positive style.
        white_rows = round(white_fraction * height)
        white_rows = max(0, min(height, white_rows))
        black_rows = height - white_rows

        # --- eval label ----------------------------------------------------
        display_score = self.eval_score if self.perspective else -self.eval_score
        label = self._format_label(display_score)

        # Truncate label if wider than the bar
        if len(label) > width:
            label = label[:width]

        # Decide which row gets the label.  Place it in the larger portion,
        # close to the boundary between black and white.
        if white_rows >= black_rows:
            # Label in white portion — the row just below the boundary.
            # Row indices: 0 = top … height-1 = bottom.
            # Black rows occupy indices [0 .. black_rows-1].
            # White rows occupy indices [black_rows .. height-1].
            label_row = black_rows  # first white row (closest to boundary)
            label_style = LABEL_ON_WHITE_STYLE
        else:
            # Label in black portion — last black row (closest to boundary).
            label_row = black_rows - 1
            label_style = LABEL_ON_BLACK_STYLE

        # Centre the label text within the row width
        pad_total = width - len(label)
        pad_left = pad_total // 2
        pad_right = pad_total - pad_left

        # --- assemble rows -------------------------------------------------
        result = Text()
        for row in range(height):
            is_white_region = row >= black_rows
            base_style = WHITE_STYLE if is_white_region else BLACK_STYLE

            if row == label_row:
                # Row with the eval label centred
                result.append(BLOCK * pad_left, base_style)
                result.append(label, label_style)
                result.append(BLOCK * pad_right, base_style)
            else:
                result.append(BLOCK * width, base_style)

            # Newline between rows (not after the last row)
            if row < height - 1:
                result.append("\n")

        return result
