#!/usr/bin/env python3
"""Build prototype/assets/js/icons.js: an inline SVG sprite injected at page load.

External <use href="sprite.svg#id"> references fail when a page is opened from disk
(file://), so the sprite ships as a string and is injected into the document instead.

Icons come from lucide-static (ISC license) plus a few SKAP-specific glyphs drawn in the
same 24px grammar (2px stroke, round caps and joins).

Usage:  python3 tools/build_icons.py [--cache DIR]
"""
from __future__ import annotations

import re
import sys
import urllib.request
from pathlib import Path

LUCIDE_VERSION = "1.48.0"
LUCIDE_URL = "https://unpkg.com/lucide-static@{version}/icons/{name}.svg"
ROOT = Path(__file__).resolve().parent.parent
OUT_FILE = ROOT / "prototype" / "assets" / "js" / "icons.js"

# order kept, repeats dropped (a repeat would put two <symbol>s with one id in the sprite)
LUCIDE_ICONS = list(dict.fromkeys("""
plus minus search sliders-horizontal layout-grid layout-list grid-2x2 rows-3 list x check
chevron-left chevron-right chevron-down chevron-up arrow-right arrow-left arrow-up-right heart
share-2 link download upload camera image image-plus trash-2 pencil eye eye-off lock mail user
users user-plus user-check settings log-out log-in message-square bell calendar calendar-days
calendar-check calendar-plus sun cloud cloud-rain cloud-sun cloud-snow cloud-drizzle snowflake
wind thermometer umbrella sparkles wand-sparkles rotate-cw rotate-ccw undo-2 redo-2 layers
bring-to-front send-to-back zoom-in zoom-out move maximize-2 copy flag ban ellipsis
ellipsis-vertical globe shirt credit-card receipt info triangle-alert circle-alert circle-check circle-plus circle-minus
refresh-cw house compass bookmark funnel tag palette leaf shuffle map-pin external-link
circle-help clock coffee shopping-bag footprints glasses gem crown lock-keyhole party-popper
flip-horizontal-2 scaling grip-vertical hand menu square-pen mouse-pointer-2 keyboard smartphone
monitor rocket badge-check key-round at-sign pause play
shield-check paperclip file-text archive-restore user-minus users-round git-branch qr-code
""".split()))

# SKAP-specific glyphs in the lucide grammar (24x24, stroke only).
CUSTOM_ICONS = {
    "hanger": '<path d="M10 5.5a2 2 0 1 1 3.1 1.7c-.7.5-1.1 1-1.1 1.8v.6"/>'
              '<path d="M12 9.6 3.4 15.5a1.4 1.4 0 0 0 .8 2.5h15.6a1.4 1.4 0 0 0 .8-2.5L12 9.6z"/>',
    "wardrobe": '<rect x="5" y="2.5" width="14" height="16" rx="1"/><path d="M12 2.5v10.5"/>'
                '<path d="M10.2 6.5v3M13.8 6.5v3"/><path d="M5 13h14"/><path d="M12 13v5.5"/>'
                '<path d="M7.5 15.8h2.5M14 15.8h2.5"/><path d="M3 21.5h18"/>',
    "hang-tag": '<path d="M3.5 12.6 11.4 4.7A2 2 0 0 1 12.8 4.1H18a2 2 0 0 1 2 2v5.2a2 2 0 0 1-.6 1.4l-7.9 7.9a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8z"/>'
                '<circle cx="16" cy="8" r="1.4"/>',
    "outfit": '<path d="M8.5 3.5 6 4.6 3 8.4l2.6 2 1.4-1.3V20h10V9.1l1.4 1.3 2.6-2-3-3.8-2.5-1.1"/>'
              '<path d="M8.5 3.5c.6 1.4 1.9 2.2 3.5 2.2s2.9-.8 3.5-2.2"/>',
    "jacket": '<path d="M9 3 5 5.5 3.5 21H9v-5.5"/><path d="M15 3l4 2.5 1.5 15.5H15v-5.5"/>'
              '<path d="M9 3l3 4.5L15 3"/><path d="M12 7.5V21"/><path d="M9 15.5h6"/>',
    "trousers": '<path d="M7 3h10l1.2 18h-4.6L12 10l-1.6 11H5.8L7 3z"/><path d="M7.2 6.5h9.6"/>',
    "dress": '<path d="M9.5 3h5l-.8 4.2L18 21H6l4.3-13.8L9.5 3z"/><path d="M10.3 7.2h3.4"/>',
    "beanie": '<path d="M5 15a7 7 0 0 1 14 0"/><rect x="4" y="15" width="16" height="4.5" rx="1.5"/><circle cx="12" cy="6" r="1.6"/>',
    "underwear": '<path d="M3.5 7h17l-1.6 4.4c-2.6.6-4.6 2.9-5.2 6.6h-3.4c-.6-3.7-2.6-6-5.2-6.6L3.5 7z"/>',
}

SYMBOL_ATTRS = 'viewBox="0 0 24 24"'


def fetch_lucide(name: str, cache: Path | None) -> str:
    if cache and (cache / f"{name}.svg").is_file():
        return (cache / f"{name}.svg").read_text(encoding="utf-8")
    url = LUCIDE_URL.format(version=LUCIDE_VERSION, name=name)
    with urllib.request.urlopen(url, timeout=20) as response:
        return response.read().decode("utf-8")


def inner_markup(svg_text: str) -> str:
    """Strip the license comment and the outer <svg> wrapper, keep the drawing elements."""
    body = re.sub(r"<!--.*?-->", "", svg_text, flags=re.S)
    match = re.search(r"<svg[^>]*>(.*)</svg>", body, flags=re.S)
    if not match:
        raise ValueError("not an svg document")
    return re.sub(r"\s+", " ", match.group(1)).strip()


def build(cache: Path | None) -> str:
    symbols = []
    for name in LUCIDE_ICONS:
        symbols.append(f'<symbol id="i-{name}" {SYMBOL_ATTRS}>{inner_markup(fetch_lucide(name, cache))}</symbol>')
    for name, markup in CUSTOM_ICONS.items():
        symbols.append(f'<symbol id="i-{name}" {SYMBOL_ATTRS}>{markup}</symbol>')
    sprite = "".join(symbols).replace("`", "")
    return (
        "/* Generated by tools/build_icons.py. Do not edit by hand.\n"
        f" * Lucide icons v{LUCIDE_VERSION} (ISC License, Copyright (c) Lucide Icons and Contributors)\n"
        " * plus SKAP glyphs: hanger, wardrobe, hang-tag, outfit and category icons. */\n"
        "(function injectSkapIcons() {\n"
        f"  const SPRITE = `<svg xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" "
        f"style=\"position:absolute;width:0;height:0;overflow:hidden\">{sprite}</svg>`;\n"
        "  const mount = () => document.body.insertAdjacentHTML('afterbegin', SPRITE);\n"
        "  if (document.body) mount(); else document.addEventListener('DOMContentLoaded', mount);\n"
        "})();\n"
    )


def main() -> None:
    cache = None
    if "--cache" in sys.argv:
        cache = Path(sys.argv[sys.argv.index("--cache") + 1])
    OUT_FILE.parent.mkdir(parents=True, exist_ok=True)
    OUT_FILE.write_text(build(cache), encoding="utf-8")
    print(f"wrote {len(LUCIDE_ICONS) + len(CUSTOM_ICONS)} icons to {OUT_FILE}")


if __name__ == "__main__":
    main()
