"""Egg renderer for the hatching ceremony.

Uses Unicode half-block characters (▀▄█) with true-color Rich markup
to render a high-detail egg at effectively double vertical resolution.
Each terminal row encodes two pixel rows via foreground/background colors.

Four crack stages (0-3) with palette-specific coloring.
"""

from __future__ import annotations

import math

# ── Egg Palettes ──────────────────────────────────────────────────────────

PALETTES = [
    {"name": "Amber Warmth", "shell": "#c8a564", "glow": "#ffc864", "crack": "#ffe0a0",
     "shell_dark": "#8a7040", "shell_light": "#e0c888", "glow_dim": "#b89048"},
    {"name": "Twilight Violet", "shell": "#7850a0", "glow": "#8ca0ff", "crack": "#c8a0ff",
     "shell_dark": "#503870", "shell_light": "#9878c0", "glow_dim": "#6878b8"},
    {"name": "Seafoam Tide", "shell": "#64b4aa", "glow": "#64f0dc", "crack": "#a0ffe0",
     "shell_dark": "#407870", "shell_light": "#88d0c8", "glow_dim": "#50b8a8"},
    {"name": "Ember Heart", "shell": "#a03c3c", "glow": "#ff8c32", "crack": "#ffb464",
     "shell_dark": "#702828", "shell_light": "#c06050", "glow_dim": "#b86830"},
    {"name": "Moonstone", "shell": "#b4b9c3", "glow": "#dce6ff", "crack": "#ffffff",
     "shell_dark": "#808890", "shell_light": "#d0d4dc", "glow_dim": "#a0a8b8"},
    {"name": "Forest Moss", "shell": "#508c46", "glow": "#c8b450", "crack": "#e0d080",
     "shell_dark": "#386030", "shell_light": "#70a868", "glow_dim": "#908040"},
]


# ── Pixel Grid Renderer ──────────────────────────────────────────────────

# Egg dimensions in "pixels" (each pixel = half a terminal row)
_W = 30  # width in columns
_H = 36  # height in pixel rows (18 terminal rows)

# Ellipse parameters
_CX = _W / 2
_CY = _H / 2
_RX = _W / 2 - 1  # horizontal radius
_RY = _H / 2 - 1  # vertical radius


def _hex_to_rgb(h: str) -> tuple[int, int, int]:
    h = h.lstrip("#")
    return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)


def _lerp_color(
    c1: tuple[int, int, int], c2: tuple[int, int, int], t: float
) -> tuple[int, int, int]:
    t = max(0.0, min(1.0, t))
    return (
        int(c1[0] + (c2[0] - c1[0]) * t),
        int(c1[1] + (c2[1] - c1[1]) * t),
        int(c1[2] + (c2[2] - c1[2]) * t),
    )


def _rgb_tag(r: int, g: int, b: int) -> str:
    return f"#{r:02x}{g:02x}{b:02x}"


def _egg_pixel(
    x: int,
    y: int,
    pal: dict[str, str],
    crack_stage: int,
) -> tuple[int, int, int] | None:
    """Return the RGB color for a pixel, or None if outside the egg."""
    # Egg shape: slightly tapered at top
    # Normalize coordinates
    nx = (x - _CX) / _RX
    ny = (y - _CY) / _RY

    # Top taper: squeeze x near the top
    taper = 1.0 - 0.25 * max(0.0, -ny)
    nx_tapered = nx / taper

    # Distance from edge (1.0 = center, 0.0 = edge)
    dist = nx_tapered * nx_tapered + ny * ny
    if dist > 1.0:
        return None

    edge_dist = 1.0 - math.sqrt(dist)

    shell = _hex_to_rgb(pal["shell"])
    shell_dark = _hex_to_rgb(pal["shell_dark"])
    shell_light = _hex_to_rgb(pal["shell_light"])
    glow = _hex_to_rgb(pal["glow"])
    glow_dim = _hex_to_rgb(pal["glow_dim"])
    crack_color = _hex_to_rgb(pal["crack"])

    # Check if pixel is on a crack
    if crack_stage > 0 and _is_crack(x, y, crack_stage):
        # Crack glow: brighter near crack center
        return crack_color

    # Base color: shell with edge darkening and highlight
    if edge_dist < 0.15:
        # Outer rim — dark shell
        color = _lerp_color(shell_dark, shell, edge_dist / 0.15)
    elif edge_dist > 0.6:
        # Inner glow
        inner_t = (edge_dist - 0.6) / 0.4
        color = _lerp_color(glow_dim, glow, inner_t)
    else:
        # Main shell body
        color = shell
        # Upper-left highlight
        highlight = max(0.0, -nx * 0.5 - ny * 0.5)
        if highlight > 0:
            color = _lerp_color(color, shell_light, min(1.0, highlight * 0.8))

    return color


def _is_crack(x: int, y: int, stage: int) -> bool:
    """Determine if a pixel falls on a crack line."""
    # Center crack line: wavy vertical
    cx = _CX + math.sin(y * 0.3) * 2.0
    dx = abs(x - cx)

    if stage >= 1 and dx < 0.8:
        # Hairline crack in upper half
        if y < _CY + 2:
            return True

    if stage >= 2:
        # Branching cracks
        branch1_x = _CX + math.sin(y * 0.25 + 1.5) * 3.0 - 3
        if abs(x - branch1_x) < 0.8 and _CY - 6 < y < _CY + 6:
            return True
        branch2_x = _CX + math.sin(y * 0.2 - 1.0) * 2.5 + 3
        if abs(x - branch2_x) < 0.8 and _CY - 4 < y < _CY + 8:
            return True

    if stage >= 3:
        # Web of cracks
        web1 = _CX + math.sin(y * 0.15 + 2.5) * 5.0
        if abs(x - web1) < 0.6 and y > _CY - 8:
            return True
        web2 = _CX + math.cos(y * 0.2 + 0.8) * 4.0
        if abs(x - web2) < 0.6:
            return True
        # Horizontal fracture
        if abs(y - _CY) < 1.0 and abs(x - _CX) < _RX * 0.6:
            return True

    return False


def render_egg(palette_index: int = 0, crack_stage: int = 0) -> str:
    """Render an egg as Rich markup using half-block characters.

    Uses ▀ (upper half block) with foreground = top pixel, background = bottom pixel
    to achieve double vertical resolution.

    Args:
        palette_index: Index into PALETTES (0-5).
        crack_stage: Crack stage 0 (intact) through 3 (breaking).

    Returns:
        Rich markup string for the egg.
    """
    pal = PALETTES[palette_index % len(PALETTES)]
    stage = max(0, min(3, crack_stage))

    # Build pixel grid
    grid: list[list[tuple[int, int, int] | None]] = []
    for y in range(_H):
        row: list[tuple[int, int, int] | None] = []
        for x in range(_W):
            row.append(_egg_pixel(x, y, pal, stage))
        grid.append(row)

    # Render as half-block pairs
    lines: list[str] = []
    for row_pair in range(0, _H, 2):
        line_parts: list[str] = []
        top_row = grid[row_pair] if row_pair < _H else [None] * _W
        bot_row = grid[row_pair + 1] if row_pair + 1 < _H else [None] * _W

        for col in range(_W):
            top = top_row[col]
            bot = bot_row[col]

            if top is None and bot is None:
                line_parts.append(" ")
            elif top is not None and bot is None:
                fg = _rgb_tag(*top)
                line_parts.append(f"[{fg}]▀[/]")
            elif top is None and bot is not None:
                fg = _rgb_tag(*bot)
                line_parts.append(f"[{fg}]▄[/]")
            else:
                assert top is not None and bot is not None
                fg = _rgb_tag(*top)
                bg = _rgb_tag(*bot)
                line_parts.append(f"[{fg} on {bg}]▀[/]")

        # Strip trailing spaces
        line = "".join(line_parts).rstrip()
        lines.append(line)

    return "\n".join(lines)
