#!/usr/bin/env python3
"""Compose the kicad-bridge hero in the Adom bridge-family style (Pup/Fusion/KiCad).

Clones the Pup hero's measured geometry exactly, drops in a fresh vitrine capture,
and ASSERTS the ink alignment before saving. See SKILL.md in this directory for the
full workflow (capture -> compose -> Hero Studio gate -> apply).

Usage:
    python3 compose_hero.py <vitrine-capture.png> <out.png>

The vitrine capture should be a real KiCad window screenshotted at exactly
1030x1025 (resize it first with desktop_set_window_bounds {"w":1030,"h":1025}).
Anything else gets LANCZOS-scaled, which softens UI text.
"""
import sys
from pathlib import Path

import numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageFont

HERE = Path(__file__).parent
PUP = HERE / "reference-pup-hero.png"          # Pup's true published asset (hero-v9)
FONTS = HERE.parent / "kicad-tour" / "hero" / "fonts"

W, H = 2000, 1250
TITLE = "KiCad"
SUB_1A, SUB_1B = "Your real KiCad, ", "driven by AI"
SUB_2A, SUB_2B = "entirely in the ", "background."   # accent word(s) in teal
TEAL = (0, 230, 220)

# Measured off Pup's asset — every bridge hero shares these (see SKILL.md "Layout numbers"):
LOGO_CROP, LOGO_AT = (60, 48, 335, 130), (60, 48)     # ADOM wordmark, verbatim from Pup
PILL_CROP, PILL_AT = (1796, 36, 1964, 108), (1796, 36)  # BRIDGE pill, verbatim from Pup
INK_EDGES = (79, 83, 83)        # logo / title / subtitle left ink edges (title SHARES the
                                 # subtitle's line — John's ruling; Pup's own hero has the title
                                 # 14px right of its subtitle, do NOT copy that stagger)
CAP_TOP = 263                    # title cap line
SUB_Y1, SUB_Y2 = 501, 565        # subtitle line tops
VITRINE_AT, VITRINE_W, VITRINE_H = (920, 150), 1030, 1025
RADIUS, SHADOW_BLUR, SHADOW_DY = 24, 28, 12


def ink_edges(im):
    a = np.array(im).astype(int)
    diff = np.abs(a - a[700, 400]).sum(axis=2) > 45
    def left(y0, y1):
        for x in range(40, 900):
            if diff[y0:y1, x].sum() > 2:
                return x
    return left(48, 132), left(263, 460), left(495, 640)


def compose(title_dx, sub_dx, pup, base, tf, sf):
    hero = base.copy()
    hero.paste(pup.crop(LOGO_CROP), LOGO_AT)
    hero.paste(pup.crop(PILL_CROP), PILL_AT)
    d = ImageDraw.Draw(hero)
    x, y = title_dx, CAP_TOP - tf.getbbox(TITLE[0])[1]
    for ch in TITLE:                      # -5px tracking, per the studio's .h-title letter-spacing
        d.text((x, y), ch, font=tf, fill=(230, 237, 243))
        x += d.textlength(ch, font=tf) - 5
    scol = (232, 238, 244)
    w = d.textlength(SUB_1A, font=sf)
    d.text((sub_dx, SUB_Y1), SUB_1A, font=sf, fill=scol)
    d.text((sub_dx + w, SUB_Y1), SUB_1B, font=sf, fill=scol)
    w = d.textlength(SUB_2A, font=sf)
    d.text((sub_dx, SUB_Y2), SUB_2A, font=sf, fill=scol)
    d.text((sub_dx + w, SUB_Y2), SUB_2B, font=sf, fill=TEAL)
    return hero


def main(capture_path, out_path):
    if not PUP.exists():
        sys.exit(f"missing {PUP}\nFetch it: adom-wiki repo download pup-bridge --extract /tmp/pup-repo\n"
                 f"then copy hero-v9.png here. (Do NOT use `repo show` — it mangles binaries.)")
    pup = Image.open(PUP).convert("RGB")
    pa = np.array(pup)
    # Background: bilinear gradient through Pup's four corner colors.
    tl, tr = pa[6, 6].astype(float), pa[6, W - 7].astype(float)
    bl, br = pa[H - 7, 6].astype(float), pa[H - 7, W - 7].astype(float)
    xs = np.linspace(0, 1, W)[None, :, None]
    ys = np.linspace(0, 1, H)[:, None, None]
    base = Image.fromarray((tl * (1 - xs) * (1 - ys) + tr * xs * (1 - ys)
                            + bl * (1 - xs) * ys + br * xs * ys).astype(np.uint8))

    # Title: Familjen Grotesk BOLD (brand doctrine, hero-studio .h-title = 700).
    # 242px makes cap height match Pup's measured 155px.
    tf = ImageFont.truetype(str(FONTS / "FamiljenGrotesk-Bold.ttf"), 242)
    sf = ImageFont.truetype(str(FONTS / "Satoshi-Regular.ttf"), 52)

    # Calibrate: fonts lie about left bearings (Familjen reported 0 for K, drew 16).
    # Render, measure actual ink, correct, re-render, assert.
    t_dx, s_dx = 98, 84
    lo, ti, su = ink_edges(compose(t_dx, s_dx, pup, base, tf, sf))
    t_dx += INK_EDGES[1] - ti
    s_dx += INK_EDGES[2] - su
    hero = compose(t_dx, s_dx, pup, base, tf, sf)
    assert ink_edges(hero) == INK_EDGES, f"alignment drifted: {ink_edges(hero)} != {INK_EDGES}"

    # Vitrine: real window capture in Pup's exact floating-window rect.
    win = Image.open(capture_path).convert("RGB")
    if win.size != (VITRINE_W, VITRINE_H):
        print(f"WARNING: capture is {win.size}, scaling to {(VITRINE_W, VITRINE_H)} (softens text; "
              f"prefer resizing the real window before capture)")
        win = win.resize((VITRINE_W, VITRINE_H), Image.LANCZOS)
    mask = Image.new("L", (VITRINE_W, VITRINE_H), 0)
    ImageDraw.Draw(mask).rounded_rectangle((0, 0, VITRINE_W - 1, VITRINE_H - 1), radius=RADIUS, fill=255)
    sh = Image.new("L", (W, H), 0)
    vx, vy = VITRINE_AT
    ImageDraw.Draw(sh).rounded_rectangle((vx, vy + SHADOW_DY, vx + VITRINE_W, vy + SHADOW_DY + VITRINE_H),
                                         radius=RADIUS, fill=110)
    sh = sh.filter(ImageFilter.GaussianBlur(SHADOW_BLUR))
    hero = Image.composite(Image.new("RGB", (W, H), (0, 0, 0)), hero, sh)
    hero.paste(win, VITRINE_AT, mask)
    hero.save(out_path)
    print(f"saved {out_path}  (alignment {INK_EDGES} verified)")


if __name__ == "__main__":
    if len(sys.argv) != 3:
        sys.exit(__doc__)
    main(sys.argv[1], sys.argv[2])