"""
R36S Manager — Design Tokens.

Single source of truth for every visual constant in the GUI.
If a value is not in this file, it does not exist. No hex codes,
no font sizes, no durations are allowed outside of here.

Reference: docs/DESIGN_PHILOSOPHY.md

Coordinate system: native pygame, top-left origin (0, 0).
Color format: (r, g, b) or (r, g, b, a) ints 0–255 for pygame.
Hex literals are documentation only; runtime values use the tuples.
"""

from __future__ import annotations

from dataclasses import dataclass


# ─── helpers ──────────────────────────────────────────────────────────


def hex_rgb(value: str) -> tuple[int, int, int]:
    """`'#0F0B1F'` → `(15, 11, 31)`. Used inside this module only."""
    v = value.lstrip("#")
    return (int(v[0:2], 16), int(v[2:4], 16), int(v[4:6], 16))


def with_alpha(rgb: tuple[int, int, int], alpha: float) -> tuple[int, int, int, int]:
    """Attach an alpha 0.0–1.0 to an RGB triple."""
    return (rgb[0], rgb[1], rgb[2], max(0, min(255, int(alpha * 255))))


# ─── viewport ────────────────────────────────────────────────────────


VIEWPORT_WIDTH = 640
VIEWPORT_HEIGHT = 480
ASPECT_RATIO = VIEWPORT_WIDTH / VIEWPORT_HEIGHT  # 1.333…


# ─── color tokens ────────────────────────────────────────────────────
#
# Vaporwave Subdued ("Twilight") palette.
# All RGB; alpha is applied at usage site via `with_alpha` or
# Surface.set_alpha (pygame).


class Color:
    # base — near-black "ink" canvas, matched to the game shell so AAhead
    # Control reads as the settings section of the same product
    BG_DEEP = hex_rgb("#0B0C0E")       # canvas base
    BG_MID = hex_rgb("#0E1013")        # between base and elevated
    BG_UP = hex_rgb("#131519")         # elevated background

    # surfaces
    SURFACE_BASE = hex_rgb("#16181C")
    SURFACE_ELEVATED = hex_rgb("#20242B")  # focused / hover state surface
    SURFACE_GLASS = hex_rgb("#16181C")     # glass tint base, paired with alpha

    # borders
    BORDER_SOFT = hex_rgb("#24272E")       # default card border
    BORDER_BRIGHT = hex_rgb("#33373F")     # divider, slightly more presence

    # text
    TEXT_PRIMARY = hex_rgb("#F4F6FA")
    TEXT_SECONDARY = hex_rgb("#AAB1BD")
    TEXT_MUTED = hex_rgb("#6B7280")
    TEXT_DISABLED = hex_rgb("#444A53")

    # signature accent triad — used together as brand identity
    ACCENT_ROSE = hex_rgb("#E89FD3")       # focus primary
    ACCENT_CYAN = hex_rgb("#7DCEDD")       # status / progress
    ACCENT_PEACH = hex_rgb("#F5C19B")      # highlight / warning

    # status colors — never bright/saturated
    SUCCESS = hex_rgb("#93D9B2")           # mint sage
    WARNING = hex_rgb("#F5C19B")           # peach (same as ACCENT_PEACH)
    ERROR = hex_rgb("#E89090")             # dusty rose, not red
    INFO = hex_rgb("#7DCEDD")              # muted cyan (same as ACCENT_CYAN)


# ─── gradients (resolved at paint time) ──────────────────────────────


@dataclass(frozen=True)
class GradientStop:
    position: float       # 0.0 → 1.0
    color: tuple[int, int, int]


@dataclass(frozen=True)
class Gradient:
    stops: tuple[GradientStop, ...]
    angle_deg: float = 180.0  # 0° = bottom-to-top, 90° = left-to-right


class Gradients:
    # ─── THE BRAND SIGNATURE ────────────────────────────────────────
    # Every focused state uses this. When the user sees rose→cyan,
    # they know: I am here, this thing is active.
    FOCUS_GLOW = Gradient(
        stops=(
            GradientStop(0.0, Color.ACCENT_ROSE),
            GradientStop(1.0, Color.ACCENT_CYAN),
        ),
        angle_deg=135.0,
    )

    # subtle depth on every card
    CARD_DEPTH = Gradient(
        stops=(
            GradientStop(0.0, Color.SURFACE_BASE),
            GradientStop(1.0, Color.BG_MID),
        ),
        angle_deg=160.0,
    )

    # hero zone background
    HERO_WARMTH = Gradient(
        stops=(
            GradientStop(0.0, Color.SURFACE_ELEVATED),
            GradientStop(0.5, Color.SURFACE_BASE),
            GradientStop(1.0, Color.BG_MID),
        ),
        angle_deg=180.0,
    )

    # full-screen ambient — slowly noise-shifted in 30s loop
    BG_AMBIENT = Gradient(
        stops=(
            GradientStop(0.0, Color.BG_UP),
            GradientStop(1.0, Color.BG_DEEP),
        ),
        angle_deg=180.0,
    )


# ─── typography ──────────────────────────────────────────────────────
#
# All TTF bundled under assets/fonts/. Loader resolves these by name.


SPACE_GROTESK_BOLD = "SpaceGrotesk-Bold.ttf"
SPACE_GROTESK_SEMIBOLD = "SpaceGrotesk-SemiBold.ttf"
INTER_REGULAR = "Inter-Regular.ttf"
INTER_MEDIUM = "Inter-Medium.ttf"
INTER_SEMIBOLD = "Inter-SemiBold.ttf"
JETBRAINS_MONO = "JetBrainsMono-Regular.ttf"
PHOSPHOR_REGULAR = "Phosphor-Regular.ttf"
PHOSPHOR_DUOTONE = "Phosphor-Duotone.ttf"


@dataclass(frozen=True)
class TextStyle:
    font: str
    size: int
    weight: str           # informational; embodied in font filename
    tracking: float = 0.0  # additional letter-spacing in px


class Type:
    HERO = TextStyle(SPACE_GROTESK_BOLD, 28, "Bold", -0.5)
    TITLE = TextStyle(SPACE_GROTESK_SEMIBOLD, 20, "SemiBold", -0.3)
    BODY = TextStyle(INTER_REGULAR, 16, "Regular", 0.0)
    BODY_STRONG = TextStyle(INTER_MEDIUM, 16, "Medium", 0.0)
    CAPTION = TextStyle(INTER_REGULAR, 13, "Regular", 0.2)
    MONO_TECH = TextStyle(JETBRAINS_MONO, 12, "Regular", 0.0)
    EYEBROW = TextStyle(INTER_SEMIBOLD, 11, "SemiBold", 1.5)  # all-caps section header

    # ── compact styles for dense cards/tiles (H1 status console etc.) ──
    STATE = TextStyle(INTER_MEDIUM, 14, "Medium", 0.0)         # one-line verdict
    LABEL_SM = TextStyle(INTER_SEMIBOLD, 12, "SemiBold", -0.1)  # card/tile label
    BODY_SM = TextStyle(INTER_REGULAR, 12, "Regular", 0.0)     # small body / queue msg
    META_SM = TextStyle(JETBRAINS_MONO, 10, "Regular", 0.0)    # tiny mono detail line


# ─── spacing (4px grid) ──────────────────────────────────────────────


class Space:
    XS = 4
    S = 8
    M = 12
    L = 16
    XL = 24
    XXL = 32
    XXXL = 48
    HERO = 64

    # safe area insets on every screen
    SAFE_AREA = L  # 16 px

    # standard grid gutter
    GUTTER = L

    # fixed regions
    STATUS_BAR_HEIGHT = 48
    FOOTER_HINT_HEIGHT = 32
    HERO_ZONE_HEIGHT = 130
    LIST_ROW_HEIGHT = 56
    LIST_ROW_HEIGHT_LARGE = 64  # for device-pair rows w/ subline


# ─── radius ──────────────────────────────────────────────────────────


class Radius:
    CARD = 12
    BUTTON = 8
    CHIP = 6
    PILL = 999  # fully rounded


# ─── stroke widths ────────────────────────────────────────────────────


class Stroke:
    HAIRLINE = 1
    BORDER_DEFAULT = 1
    BORDER_FOCUSED = 2
    DIVIDER = 1


# ─── motion ──────────────────────────────────────────────────────────


class Ease:
    """
    Cubic-bezier parameters. Used by gui.shared.anim helpers.
    Format matches CSS / web conventions: (x1, y1, x2, y2).
    """

    OUT_EXPO = (0.16, 1.0, 0.3, 1.0)        # primary
    OUT_QUART = (0.25, 1.0, 0.5, 1.0)       # backup
    IN_OUT = (0.83, 0.0, 0.17, 1.0)         # screen transitions
    PRESS = (0.4, 0.0, 0.2, 1.0)            # button press feedback


class Dur:
    """Animation durations in milliseconds."""

    MICRO = 100           # press feedback
    FAST = 180            # focus enter / exit
    BASE = 280            # card transitions, dialog open
    SLOW = 400            # screen-level transitions
    HERO = 600            # initial mount stagger
    SHIMMER_LOOP = 1200   # loading shimmer sweep period
    AMBIENT_LOOP = 30000  # background gradient drift loop


class Stagger:
    LIST_ITEM = 40        # ms between successive list-item enters


# ─── focus state metrics ──────────────────────────────────────────────


class Focus:
    """Numbers that define how a focused element looks."""

    SCALE = 1.03                  # uniform scale multiplier
    BORDER_PX = Stroke.BORDER_FOCUSED
    OUTER_GLOW_PX = 12            # blur radius of the outer glow
    OUTER_GLOW_ALPHA = 0.30       # 0.0–1.0
    BG_BRIGHTEN_FROM = Color.SURFACE_BASE
    BG_BRIGHTEN_TO = Color.SURFACE_ELEVATED


# ─── icon sizing ─────────────────────────────────────────────────────


class IconSize:
    INLINE = 20
    LIST = 28
    TILE = 40
    HERO = 56
    EMPTY_STATE = 64


# ─── sound + haptics specs (consumed by gui.shared.feedback) ──────────


class Sound:
    """Filenames in assets/sounds/. Volumes applied at playback."""

    FOCUS_MOVE = ("tick.wav", 0.5)        # very soft
    CONFIRM = ("chime.wav", 0.7)
    BACK = ("pop.wav", 0.6)
    ERROR = ("dip.wav", 0.6)
    SUCCESS = ("bloom.wav", 0.7)
    LOADING_START = ("whoosh.wav", 0.5)


class Haptic:
    """Rumble pattern in milliseconds. Played via evdev / sysfs rumble."""

    # (duration_ms, strength_0_to_255) — single pulse
    CONFIRM = ((50, 100),)
    SUCCESS = ((80, 120),)
    # multi-pulse sequence with gaps
    ERROR = ((30, 140), (30, 0), (30, 140))


# ─── status bar layout (consumed by Home and Module screens) ──────────


class StatusBar:
    HEIGHT = Space.STATUS_BAR_HEIGHT
    HORIZONTAL_PADDING = Space.L
    CHIP_GAP = Space.M


class FooterHint:
    HEIGHT = Space.FOOTER_HINT_HEIGHT
    HORIZONTAL_PADDING = Space.L


# ─── home grid layout ────────────────────────────────────────────────


class HomeGrid:
    COLUMNS = 3
    ROWS = 2
    GUTTER = Space.GUTTER

    # derived later from the viewport in the screen module so we don't
    # hardcode tile pixel dimensions here (allows future viewport changes)


# ─── do not edit at runtime ──────────────────────────────────────────


def freeze() -> None:
    """No-op. Exists so the module's intent is clear: read-only."""
    return None
