#!/usr/bin/env -S uv run --with fonttools --with brotli --with uharfbuzz --quiet python
"""Politick "Broadcast · Soft red" — the canonical LOGOTYPE (outlined wordmark + tagline).

This is the font-INDEPENDENT, locked brand wordmark. `Logo.svelte` renders "Politick" as
LIVE text for the running app; this generator instead OUTLINES the word to true vector
`<path>` data so the official logo files are pixel-identical everywhere and carry no font
dependency (favicons, OG images, print, embeds, partner usage).

METHOD (true outlines, real kerning — never raster, never sampled)
------------------------------------------------------------------
1. The self-hosted face `design/system/tokens/fonts/playfair-600-normal-latin.woff2` is the
   upstream Playfair Display VARIABLE font (wght 400..900) subset to latin. We instantiate it
   at **wght=600 (SemiBold)** with fontTools `instancer` -> a static font in memory.
2. We shape the string with **uharfbuzz** (HarfBuzz) against THOSE SAME bytes, so glyph
   positions and GPOS **kerning** ('ti', 'ck' pairs, etc.) are correct — not monospaced.
3. Each glyph outline is pulled with fontTools `SVGPathPen` and drawn through a single
   `TransformPen` that BAKES (a) the shaped pen position and (b) the global font->SVG flip +
   tight normalisation, so the emitted path data is already in final SVG coordinates.
4. The result is normalised to a tight `viewBox`. Geometry is exact — the only inputs are the
   font, the weight, and a small `Params` block. Re-runnable.

The wordmark fill is `currentColor` (ink) — NO red in the letters (project decision: the
wordmark is all ink). The tagline is outlined the same way in IBM Plex Sans and split into an
ink group and a soft-red (`var(--pk-accent, #B84A38)`) group for the phrase "ON RECORD.".
"""
from __future__ import annotations

import io
import sys
from dataclasses import dataclass
from pathlib import Path

import uharfbuzz as hb
from fontTools.misc.transform import Transform
from fontTools.pens.boundsPen import BoundsPen
from fontTools.pens.svgPathPen import SVGPathPen
from fontTools.pens.transformPen import TransformPen
from fontTools.ttLib import TTFont
from fontTools.varLib.instancer import instantiateVariableFont

OUT = Path(__file__).parent
FONTS = OUT.parent / "tokens" / "fonts"

sys.path.insert(0, str(OUT))
import mark as markmod   # the LOCKED Wave-1 vector mark (do NOT modify it)

DISPLAY_WOFF2 = FONTS / "playfair-600-normal-latin.woff2"   # variable; instanced at 600
SANS_WOFF2 = FONTS / "plexsans-600-normal-latin.woff2"      # IBM Plex Sans SemiBold (static)

# --------------------------------------------------------------------------- brand colours
INK = "#1C2024"            # ink
PAPER = "#F6F3EC"          # warm paper
ACCENT = "#B84A38"         # soft red
ACCENT_VAR = f"var(--pk-accent, {ACCENT})"

# --------------------------------------------------------------------------- text content
WORD = "Politick"
TAGLINE = "PARLIAMENT. ON RECORD. FOR EVERY CITIZEN."
TAGLINE_RED = "ON RECORD."   # the only soft-red run in the tagline


# --------------------------------------------------------------------------- params
@dataclass(frozen=True)
class Params:
    """Single source of truth. Lengths are in font em units (upem = 1000): pure ratios of the
    type size, never device pixels."""
    word_weight: int = 600           # Playfair Display SemiBold (CONFIRMED)
    word_tracking: float = 0.0       # extra letter-spacing, em (Playfair is already tight)
    tag_weight: int = 500            # IBM Plex Sans Medium (matches the reference's light caps)
    tag_tracking: float = 0.18       # tracked small-caps look, em
    decimals: int = 2                # path coordinate precision

    # --- lockup composition (all ratios of the wordmark's ascender->baseline height WH) ---
    mark_ratio_h: float = 1.46       # mark height / WH for the HORIZONTAL lockup
    mark_ratio_s: float = 1.62       # mark height / WH for the STACKED lockup (mark leads)
    gap_h: float = 0.34              # mark->wordmark gap (horizontal) / WH
    gap_s: float = 0.30              # mark->wordmark gap (stacked, vertical) / WH
    pad_lock: float = 0.30           # clear-space padding around a lockup / WH
    # --- tagline block (rule + tracked caps, set to span the wordmark width) ---
    tag_gap: float = 0.30            # wordmark baseline -> tagline cap-top / WH
    rule_len_cap: float = 1.7        # red rule length / tagline cap-height
    rule_gap_cap: float = 0.95       # gap rule->text / tagline cap-height
    rule_weight_cap: float = 0.12    # red rule stroke weight / tagline cap-height


P = Params()


# --------------------------------------------------------------------------- float fmt
def f(x: float, nd: int = P.decimals) -> str:
    """Compact float formatting (matches the house generator style)."""
    s = f"{x:.{nd}f}".rstrip("0").rstrip(".")
    return s if s not in ("", "-0") else "0"


# --------------------------------------------------------------------------- font loading
def load_static(woff2: Path, weight: int) -> bytes:
    """Instantiate a variable woff2 at `weight` -> raw static-font bytes. The SAME bytes feed
    both HarfBuzz (shaping/kerning) and fontTools (outline extraction) so they never disagree.
    A face with no `fvar` is already static and re-saved unchanged."""
    tt = TTFont(woff2)
    if "fvar" in tt:
        instantiateVariableFont(tt, {"wght": weight}, inplace=True)
    tt.flavor = None          # save as plain SFNT — this HarfBuzz build can't read woff2
    buf = io.BytesIO()
    tt.save(buf)
    return buf.getvalue()


# --------------------------------------------------------------------------- shaping
def shape(raw: bytes, text: str, tracking_em: float) -> list[dict]:
    """Shape `text` with HarfBuzz against `raw`. Returns placed glyphs: glyph name, pen
    position (font units, y-up), source cluster. `tracking_em` adds even letter-spacing (em)
    after each glyph (for the tracked tagline). The pen advances by the shaped x_advance so
    GPOS kerning is preserved between pairs."""
    face = hb.Face(raw)
    font = hb.Font(face)
    upem = face.upem
    tracking = tracking_em * upem

    buf = hb.Buffer()
    buf.add_str(text)
    buf.guess_segment_properties()              # LTR, Latin, default language
    hb.shape(font, buf, {"kern": True, "liga": True})

    order = TTFont(io.BytesIO(raw)).getGlyphOrder()
    placed: list[dict] = []
    x = 0.0
    for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
        placed.append(dict(name=order[info.codepoint], px=x + pos.x_offset,
                           py=pos.y_offset, cluster=info.cluster))
        x += pos.x_advance + tracking
    return placed


# --------------------------------------------------------------------------- outline a run
def outline_run(woff2: Path, weight: int, text: str, tracking_em: float, pad_em: float = 0.0):
    """Shape + outline `text`. Returns (glyph_records, viewbox) where viewbox = (x,y,w,h) in
    font units and each record carries its final-SVG `<path>` data, source cluster and char.
    The flip (font y-up -> SVG y-down) and tight normalisation are baked into the path data."""
    raw = load_static(woff2, weight)
    placed = shape(raw, text, tracking_em)
    tt = TTFont(io.BytesIO(raw))
    glyphset = tt.getGlyphSet()
    upem = tt["head"].unitsPerEm
    pad = pad_em * upem

    # pass 1 — tight union bbox in font units, y-up (includes curve extrema + placement)
    bp = BoundsPen(glyphset)
    for g in placed:
        glyphset[g["name"]].draw(TransformPen(bp, Transform().translate(g["px"], g["py"])))
    if bp.bounds is None:
        raise RuntimeError(f"empty outline bounds for {text!r}")
    xmin, ymin, xmax, ymax = bp.bounds

    # global font->SVG transform: place, flip y, normalise so (xmin,ymax) -> (pad,pad)
    g_norm = Transform().translate(pad - xmin, pad + ymax).scale(1, -1)

    # pass 2 — emit each glyph's path already in final SVG coordinates
    recs = []
    for g in placed:
        pen = SVGPathPen(glyphset)
        tp = TransformPen(pen, g_norm.translate(g["px"], g["py"]))
        glyphset[g["name"]].draw(tp)
        d = pen.getCommands()
        if not d:
            continue                               # spaces / non-drawing glyphs
        recs.append(dict(d=d, cluster=g["cluster"], char=text[g["cluster"]]))

    vb = (0.0, 0.0, (xmax - xmin) + 2 * pad, (ymax - ymin) + 2 * pad)
    return recs, vb


def join_paths(recs) -> str:
    return " ".join(r["d"] for r in recs)


# --------------------------------------------------------------------------- wordmark svg
def build_wordmark_recs():
    return outline_run(DISPLAY_WOFF2, P.word_weight, WORD, P.word_tracking)


def emit_wordmark() -> str:
    recs, vb = build_wordmark_recs()
    d = join_paths(recs)
    return f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="{f(vb[0])} {f(vb[1])} {f(vb[2])} {f(vb[3])}"
     role="img" aria-label="Politick" style="color:{INK}">
  <path d="{d}" fill="currentColor"/>
</svg>
'''


# --------------------------------------------------------------------------- tagline svg pieces
def _red_clusters(text: str, sub: str) -> set[int]:
    """Character indices in `text` that belong to the (first) occurrence of `sub`."""
    i = text.index(sub)
    return set(range(i, i + len(sub)))


def build_tagline_recs():
    recs, vb = outline_run(SANS_WOFF2, P.tag_weight, TAGLINE, P.tag_tracking)
    red = _red_clusters(TAGLINE, TAGLINE_RED)
    ink_d = " ".join(r["d"] for r in recs if r["cluster"] not in red)
    red_d = " ".join(r["d"] for r in recs if r["cluster"] in red)
    return ink_d, red_d, vb


# --------------------------------------------------------------------------- mark placement
def mark_geometry():
    """Build the LOCKED Wave-1 mark and return (inner_svg_markup, bbox) where bbox = (x,y,w,h)
    in the mark's own user units, with the accent dot themeable and strokes = currentColor."""
    mp = markmod.Params()
    m = markmod.build_mark(mp)
    markup = markmod._mark_group(m, mp, accent=markmod.ACCENT_VAR, ink="currentColor")
    return markup, m["bbox"]


def placed_mark(markup: str, bbox, x: float, y: float, s: float) -> str:
    """Wrap the mark markup in a <g> that scales by `s` and puts its bbox top-left at (x,y)."""
    ml, mt, _, _ = bbox
    return f'<g transform="translate({f(x - s * ml)} {f(y - s * mt)}) scale({f(s, 4)})">{markup}</g>'


def placed_wordmark(recs, x: float, y: float, s: float = 1.0) -> str:
    d = join_paths(recs)
    return (f'<g transform="translate({f(x)} {f(y)}) scale({f(s, 4)})">'
            f'<path d="{d}" fill="currentColor"/></g>')


def _svg(vb, body: str, label: str) -> str:
    return f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="{f(vb[0])} {f(vb[1])} {f(vb[2])} {f(vb[3])}"
     role="img" aria-label="{label}" style="color:{INK}">
  {body}
</svg>
'''


# --------------------------------------------------------------------------- tagline block
def tagline_block(x: float, top: float, width: float):
    """Compose the tagline block (leading red rule + tracked caps with 'ON RECORD.' in accent),
    LEFT-aligned at x, cap-top at `top`, scaled so rule+gap+text spans exactly `width`.
    Returns (svg_markup, cap_height, bottom_y)."""
    ink_d, red_d, vb = build_tagline_recs()
    tag_w, tag_h = vb[2], vb[3]                 # text natural width, cap height (font units)
    # width = rule_len + rule_gap + s*tag_w ; rule_len = rl*cap, rule_gap = rg*cap, cap = s*tag_h
    rl, rg = P.rule_len_cap, P.rule_gap_cap
    s = width / ((rl + rg) * tag_h + tag_w)
    cap = s * tag_h
    rule_len = rl * cap
    rule_gap = rg * cap
    text_x = x + rule_len + rule_gap
    # the tagline paths are in their own vb (top-left origin); place at text_x, top
    g_ink = (f'<g transform="translate({f(text_x)} {f(top)}) scale({f(s, 4)})">'
             f'<path d="{ink_d}" fill="currentColor"/></g>')
    g_red = (f'<g transform="translate({f(text_x)} {f(top)}) scale({f(s, 4)})">'
             f'<path d="{red_d}" fill="{ACCENT_VAR}"/></g>')
    rule_y = top + cap * 0.5                     # vertically centred on the cap height
    rule_w = P.rule_weight_cap * cap
    rule = (f'<line x1="{f(x)}" y1="{f(rule_y)}" x2="{f(x + rule_len)}" y2="{f(rule_y)}" '
            f'stroke="{ACCENT_VAR}" stroke-width="{f(rule_w)}"/>')
    return rule + g_ink + g_red, cap, top + cap


# --------------------------------------------------------------------------- lockups
def emit_horizontal(tagline: bool) -> str:
    recs, vb = build_wordmark_recs()
    WW, WH = vb[2], vb[3]
    markup, mbox = mark_geometry()
    mh = mbox[3]
    s_mark = (P.mark_ratio_h * WH) / mh
    MW, MH = s_mark * mbox[2], P.mark_ratio_h * WH
    gap = P.gap_h * WH

    # wordmark top-left at (0,0); mark to the left, vertically centred on the wordmark
    word_x, word_y = 0.0, 0.0
    mark_x = -(gap + MW)
    mark_y = WH / 2 - MH / 2
    body = placed_mark(markup, mbox, mark_x, mark_y, s_mark) + placed_wordmark(recs, word_x, word_y)

    content_top = min(word_y, mark_y)
    content_bot = max(word_y + WH, mark_y + MH)
    if tagline:
        tag_top = WH + P.tag_gap * WH
        tag_svg, _, tag_bot = tagline_block(word_x, tag_top, WW)
        body += tag_svg
        content_bot = max(content_bot, tag_bot)

    pad = P.pad_lock * WH
    left, right = mark_x, WW
    vbx = (left - pad, content_top - pad, (right - left) + 2 * pad, (content_bot - content_top) + 2 * pad)
    return _svg(vbx, body, "Politick")


def emit_stacked(tagline: bool) -> str:
    recs, vb = build_wordmark_recs()
    WW, WH = vb[2], vb[3]
    markup, mbox = mark_geometry()
    mh = mbox[3]
    s_mark = (P.mark_ratio_s * WH) / mh
    MW, MH = s_mark * mbox[2], P.mark_ratio_s * WH
    gap = P.gap_s * WH

    # mark centred on top; wordmark centred below
    cx = 0.0                                     # shared centre x
    mark_x = cx - MW / 2
    mark_y = 0.0
    word_x = cx - WW / 2
    word_y = MH + gap
    body = placed_mark(markup, mbox, mark_x, mark_y, s_mark) + placed_wordmark(recs, word_x, word_y)

    content_bot = word_y + WH
    if tagline:
        tag_top = content_bot + P.tag_gap * WH
        tag_svg, _, tag_bot = tagline_block(word_x, tag_top, WW)
        body += tag_svg
        content_bot = tag_bot

    pad = P.pad_lock * WH
    left = min(mark_x, word_x)
    right = max(mark_x + MW, word_x + WW)
    vbx = (left - pad, mark_y - pad, (right - left) + 2 * pad, (content_bot - mark_y) + 2 * pad)
    return _svg(vbx, body, "Politick")


def main():
    (OUT / "wordmark.svg").write_text(emit_wordmark())
    (OUT / "logo-horizontal.svg").write_text(emit_horizontal(tagline=False))
    (OUT / "logo-stacked.svg").write_text(emit_stacked(tagline=False))
    (OUT / "logo-horizontal-tagline.svg").write_text(emit_horizontal(tagline=True))
    (OUT / "logo-stacked-tagline.svg").write_text(emit_stacked(tagline=True))
    recs, vb = build_wordmark_recs()
    print(f"wrote wordmark.svg + 4 lockups  glyphs={len(recs)}  wm viewBox≈{f(vb[2])}x{f(vb[3])}")
    pairs = "".join(r["char"] for r in recs)
    print(f"  shaped chars: {pairs!r}")
    _, _, tvb = build_tagline_recs()
    print(f"  tagline natural w×capH (font units): {f(tvb[2])}×{f(tvb[3])}")


if __name__ == "__main__":
    main()
