"""
Low-level rendering helpers that the design language needs but pygame
doesn't ship: linear/radial gradients, rounded-rect gradient fills,
the signature rose→cyan focus glow, and a gradient border.

Everything here is pure-surface (no display dependency) so it can be
unit-rendered headlessly and blitted by screens.
"""

from __future__ import annotations

import math
from functools import lru_cache

import pygame

from ..design import tokens as t


# ─── gradients ───────────────────────────────────────────────────────


def linear_gradient(
    size: tuple[int, int],
    color_a: tuple[int, int, int],
    color_b: tuple[int, int, int],
    angle_deg: float = 180.0,
) -> pygame.Surface:
    """
    Linear gradient from color_a → color_b across `size` at `angle_deg`.
    0° = bottom→top, 90° = left→right, 180° = top→bottom (CSS-ish).
    Implemented by rendering a small gradient and scaling (cheap + smooth).
    """
    w, h = size
    # Render the gradient along the vertical axis of a tall 1px strip, then
    # rotate. For our angles (135/160/180) we approximate with a per-row or
    # per-diagonal fill at full res — fast enough at 640×480.
    surf = pygame.Surface((w, h)).convert()
    rad = math.radians(angle_deg)
    dx, dy = math.cos(rad), -math.sin(rad)
    # project each corner onto the gradient axis to normalize 0..1
    corners = [(0, 0), (w, 0), (0, h), (w, h)]
    projs = [cx * dx + cy * dy for cx, cy in corners]
    pmin, pmax = min(projs), max(projs)
    span = (pmax - pmin) or 1.0

    # Per-row is enough for near-vertical; for diagonals do per-pixel column
    # bands. We use a vertical strip + rotation for speed on pure verticals,
    # else a coarse diagonal fill.
    if abs(angle_deg % 180) < 1e-3:  # vertical
        for y in range(h):
            tt = (y - 0) / max(h - 1, 1)
            surf.fill(_lerp(color_a, color_b, tt), (0, y, w, 1))
        return surf

    # diagonal: compute per scan-row using corner projection (good enough)
    for y in range(h):
        # sample at row center x=w/2 to pick a representative t for the row
        p = (w * 0.5) * dx + y * dy
        tt = (p - pmin) / span
        surf.fill(_lerp(color_a, color_b, tt), (0, y, w, 1))
    return surf


def radial_gradient(
    size: tuple[int, int],
    center_frac: tuple[float, float],
    inner: tuple[int, int, int],
    outer: tuple[int, int, int],
) -> pygame.Surface:
    """Radial gradient inner→outer, center given as (fx, fy) fractions."""
    w, h = size
    cx, cy = center_frac[0] * w, center_frac[1] * h
    max_d = max(
        math.hypot(cx, cy),
        math.hypot(w - cx, cy),
        math.hypot(cx, h - cy),
        math.hypot(w - cx, h - cy),
    ) or 1.0
    # Build at quarter res for speed, then smoothscale up.
    sw, sh = max(w // 3, 1), max(h // 3, 1)
    small = pygame.Surface((sw, sh)).convert()
    for y in range(sh):
        for x in range(sw):
            fx, fy = x / sw * w, y / sh * h
            d = math.hypot(fx - cx, fy - cy) / max_d
            small.set_at((x, y), _lerp(inner, outer, min(d, 1.0)))
    return pygame.transform.smoothscale(small, (w, h))


def _lerp(a, b, tt):
    tt = max(0.0, min(1.0, tt))
    return (
        int(a[0] + (b[0] - a[0]) * tt),
        int(a[1] + (b[1] - a[1]) * tt),
        int(a[2] + (b[2] - a[2]) * tt),
    )


# ─── rounded rect masks / fills ──────────────────────────────────────


@lru_cache(maxsize=64)
def _round_mask(w: int, h: int, radius: int) -> pygame.Surface:
    """White rounded-rect on transparent — used as an alpha mask."""
    mask = pygame.Surface((w, h), pygame.SRCALPHA)
    pygame.draw.rect(mask, (255, 255, 255, 255), (0, 0, w, h), border_radius=radius)
    return mask


def rounded_gradient(
    size: tuple[int, int],
    color_a: tuple[int, int, int],
    color_b: tuple[int, int, int],
    radius: int,
    angle_deg: float = 160.0,
) -> pygame.Surface:
    """A rounded-rect filled with a linear gradient (e.g. card-depth)."""
    w, h = size
    grad = linear_gradient((w, h), color_a, color_b, angle_deg).convert_alpha()
    mask = _round_mask(w, h, radius)
    grad.blit(mask, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
    return grad


def rounded_fill(
    size: tuple[int, int],
    color: tuple[int, int, int],
    radius: int,
) -> pygame.Surface:
    surf = pygame.Surface(size, pygame.SRCALPHA)
    pygame.draw.rect(surf, color, (0, 0, size[0], size[1]), border_radius=radius)
    return surf


# ─── focus glow + gradient border (THE signature) ────────────────────


def focus_glow(
    rect: pygame.Rect,
    radius: int,
    *,
    spread: int = t.Focus.OUTER_GLOW_PX,
) -> tuple[pygame.Surface, tuple[int, int]]:
    """
    The rose→cyan outer glow drawn behind a focused element.
    Returns (surface, topleft) ready to blit. The surface is larger than
    rect by `spread` on each side.
    """
    pad = spread + 6
    w, h = rect.width + pad * 2, rect.height + pad * 2
    glow = pygame.Surface((w, h), pygame.SRCALPHA)

    # Concentric rounded rects, decreasing alpha — a cheap, smooth bloom.
    layers = 6
    for i in range(layers, 0, -1):
        frac = i / layers
        grow = int(spread * frac)
        # blend rose (top-left bias) and cyan (bottom-right bias)
        col = _lerp(t.Color.ACCENT_ROSE, t.Color.ACCENT_CYAN, 1 - frac)
        alpha = int(t.Focus.OUTER_GLOW_ALPHA * 255 * (frac ** 1.6) / layers * 2.2)
        layer_rect = pygame.Rect(
            pad - grow, pad - grow,
            rect.width + grow * 2, rect.height + grow * 2,
        )
        pygame.draw.rect(
            glow, (col[0], col[1], col[2], alpha), layer_rect,
            border_radius=radius + grow,
        )
    return glow, (rect.x - pad, rect.y - pad)


def gradient_border(
    size: tuple[int, int],
    radius: int,
    width: int = t.Focus.BORDER_PX,
    angle_deg: float = 135.0,
) -> pygame.Surface:
    """
    A rounded-rect *border* (ring) filled with the focus-glow gradient.
    Drawn by filling a gradient rounded-rect then punching out the inner
    area, leaving a `width`-px gradient ring.
    """
    w, h = size
    outer = rounded_gradient(
        (w, h), t.Color.ACCENT_ROSE, t.Color.ACCENT_CYAN, radius, angle_deg
    )
    # punch a transparent inner rounded-rect
    inner = pygame.Surface((w, h), pygame.SRCALPHA)
    pygame.draw.rect(
        inner, (0, 0, 0, 255),
        (width, width, w - width * 2, h - width * 2),
        border_radius=max(radius - width, 0),
    )
    outer.blit(inner, (0, 0), special_flags=pygame.BLEND_RGBA_SUB)
    return outer


# ─── scanline / grain overlay (subtle, surfaces vibe) ────────────────


@lru_cache(maxsize=4)
def grain_overlay(w: int, h: int, alpha: int = 12) -> pygame.Surface:
    """A faint noise overlay; cached for the full stage size."""
    import random

    surf = pygame.Surface((w, h), pygame.SRCALPHA)
    rng = random.Random(0xC5F)  # deterministic
    px = pygame.PixelArray(surf)
    for y in range(0, h, 2):
        for x in range(0, w, 2):
            v = rng.randint(0, 255)
            px[x, y] = (v, v, v, alpha)
    del px
    return surf
