# -*- coding: utf-8 -*-
"""Shared primitives for generating the PayGo FigCraft build script."""
import json
from svgelements import Path as SvgPath

CMDS = []
_NEXT = [2]          # node id 1 is Page 1


def _alloc():
    i = _NEXT[0]
    _NEXT[0] += 1
    return i


def cmd(_command, **params):
    CMDS.append({"command": _command, "params": params})


def create(ntype, parent=None, name=None, x=None, y=None, width=None, height=None,
           characters=None, path=None, props=None, index=None):
    p = {"type": ntype, "select": False}
    for k, v in (("parent", parent), ("name", name), ("x", x), ("y", y),
                 ("width", width), ("height", height), ("characters", characters),
                 ("path", path), ("index", index)):
        if v is not None:
            p[k] = v
    if props:
        p["props"] = props
    CMDS.append({"command": "node.create", "params": p})
    return _alloc()


def instance(component, size=1, parent=None, x=None, y=None):
    """`size` = how many nodes the main component's subtree holds; an instance
    clones all of them, so that many ids are consumed."""
    p = {"component": component}
    for k, v in (("parent", parent), ("x", x), ("y", y)):
        if v is not None:
            p[k] = v
    CMDS.append({"command": "instance.create", "params": p})
    root = _alloc()
    for _ in range(size - 1):
        _alloc()
    return root


def mark():
    return _NEXT[0]


def since(start):
    return _NEXT[0] - start


def add_page(name):
    CMDS.append({"command": "page.add", "params": {"name": name}})
    return _alloc()


def nset(ids, **props):
    if isinstance(ids, int):
        ids = [ids]
    cmd("node.set", ids=ids, **props)


def make_component(nid):
    cmd("component.create", ids=[nid])
    return nid


def dump(path):
    with open(path, "w", encoding="utf-8") as f:
        json.dump(CMDS, f, ensure_ascii=False)
    return len(CMDS)


# --------------------------------------------------------------------------
# Tokens
# --------------------------------------------------------------------------
BLUE        = "#1B92CE"
BLUE_MID    = "#1A6CA8"
BLUE_DEEP   = "#195A9E"
BLUE_DARK   = "#04507F"
GREEN       = "#69CE57"
GREEN_MID   = "#5FAD4A"
GREEN_DEEP  = "#4E893B"
INK         = "#141414"
PAPER       = "#FFFFFF"
BG          = "#F4F6F9"
LINE        = "#DDE4EC"
MUTED       = "#5B6B7C"
DANGER      = "#B3261E"
DANGER_SOFT = "#FBECEA"
DANGER_LINE = "#F0C6C1"
TINT        = "#EEF7FC"
TINT_LINE   = "#C9E4F2"
TINT_2      = "#E9F4FB"
SUCCESS_BG  = "#E7F6E4"
PENDING_BG  = "#FFF3D6"
PENDING_INK = "#7A5600"
ON_GREEN    = "#06283F"
LOGO_BLUE   = "#1E64A7"
LOGO_GREEN  = "#5CA032"

FONT = "Poppins"

SCREEN_W = 390
SCREEN_H = 844
PAD = 20            # screen side padding
R_CARD = 16
R_BTN = 14
R_INPUT = 12

CARD_SHADOW = [{"type": "dropShadow", "color": "#14141410", "offset": [0, 6],
                "radius": 18, "spread": 0, "visible": True}]
PHONE_SHADOW = [{"type": "dropShadow", "color": "#1414142E", "offset": [0, 24],
                 "radius": 48, "spread": 0, "visible": True}]


# --------------------------------------------------------------------------
# Text helpers
# --------------------------------------------------------------------------
def txt(parent, chars, size=14, style="Regular", color=INK, align=None,
        tracking=None, line_height=None, name=None, fill_width=False,
        case=None, decoration=None, width=None):
    props = {"fontFamily": FONT, "fontStyle": style, "fontSize": size, "fill": color}
    if align:
        props["textAlign"] = align
    if tracking is not None:
        props["letterSpacing"] = tracking
    if line_height is not None:
        props["lineHeight"] = line_height
    if case:
        props["textCase"] = case
    if decoration:
        props["textDecoration"] = decoration
    if fill_width:
        props["sizingH"] = "fill"
        props["textAutoResize"] = "height"
    return create("text", parent=parent, name=name or chars[:40],
                  characters=chars, width=width, props=props)


def meta(parent, label, value, size=13, color=INK, label_color=None):
    """'Label:' in SemiBold + ' value' in Regular, one text node."""
    s = label + " " + value
    nid = txt(parent, s, size=size, style="Regular", color=color,
              fill_width=True, name=label.rstrip(":"))
    styles = {"fontStyle": "SemiBold"}
    if label_color:
        styles["textFills"] = [label_color]
    cmd("text.style", id=nid, start=0, end=len(label), **styles)
    return nid


def overline(parent, s, color=BLUE_DEEP, size=11):
    return txt(parent, s, size=size, style="Bold", color=color,
               tracking="12%", case="upper", name="Overline")


# --------------------------------------------------------------------------
# Geometry helpers
# --------------------------------------------------------------------------
def circle_path(cx, cy, r):
    return (f"M{cx - r} {cy}a{r} {r} 0 1 0 {2 * r} 0a{r} {r} 0 1 0 {-2 * r} 0Z")


def rrect_path(x, y, w, h, r):
    r = min(r, w / 2, h / 2)
    return (f"M{x + r} {y}H{x + w - r}A{r} {r} 0 0 1 {x + w} {y + r}"
            f"V{y + h - r}A{r} {r} 0 0 1 {x + w - r} {y + h}"
            f"H{x + r}A{r} {r} 0 0 1 {x} {y + h - r}"
            f"V{y + r}A{r} {r} 0 0 1 {x + r} {y}Z")


_BBOX = {}


def bbox(d):
    if d not in _BBOX:
        x0, y0, x1, y1 = SvgPath(d).bbox()
        _BBOX[d] = (float(x0), float(y0), float(x1 - x0), float(y1 - y0))
    return _BBOX[d]


# --------------------------------------------------------------------------
# Icon set — 24x24 stroke icons lifted from the HTML design
# --------------------------------------------------------------------------
ICONS = {
    "home": "M3 11.5 12 4l9 7.5M5.5 9.5V20h13V9.5",
    "receipt": "M6 3h12v18l-2.4-1.5L13.2 21 10.8 19.5 8.4 21 6 19.5V3zM9 8h6M9 12h6",
    "gear": circle_path(12, 12, 3.1) + (
        "M19.1 14.6a1.6 1.6 0 0 0 .32 1.77l.06.06a2 2 0 1 1-2.76 2.76l-.06-.06"
        "a1.6 1.6 0 0 0-1.77-.32 1.6 1.6 0 0 0-.97 1.46V21a2 2 0 0 1-4 0v-.1"
        "a1.6 1.6 0 0 0-1.05-1.46 1.6 1.6 0 0 0-1.77.32l-.06.06a2 2 0 1 1-2.76-2.76"
        "l.06-.06a1.6 1.6 0 0 0 .32-1.77 1.6 1.6 0 0 0-1.46-.97H3a2 2 0 0 1 0-4h.1"
        "a1.6 1.6 0 0 0 1.46-1.05 1.6 1.6 0 0 0-.32-1.77l-.06-.06a2 2 0 1 1 2.76-2.76"
        "l.06.06a1.6 1.6 0 0 0 1.77.32H9a1.6 1.6 0 0 0 .97-1.46V3a2 2 0 0 1 4 0v.1"
        "a1.6 1.6 0 0 0 .97 1.46 1.6 1.6 0 0 0 1.77-.32l.06-.06a2 2 0 1 1 2.76 2.76"
        "l-.06.06a1.6 1.6 0 0 0-.32 1.77V9a1.6 1.6 0 0 0 1.46.97H21a2 2 0 0 1 0 4h-.1"
        "a1.6 1.6 0 0 0-1.46.97z"),
    "nfc": "M5.5 8.5a8 8 0 0 0 0 7M9.2 6a12 12 0 0 0 0 12M12.9 3.8a15.4 15.4 0 0 0 0 16.4M17.5 7v10",
    "qr": (rrect_path(3, 3, 7, 7, 1) + rrect_path(14, 3, 7, 7, 1) + rrect_path(3, 14, 7, 7, 1)
           + "M14 14h3v3h-3zM20 14h1v3h-3v1h3v1M14 20h3v1h1"),
    "share": "M12 14V4M7.5 8.5 12 4l4.5 4.5M5 12v7h14v-7",
    "check": "M20 6 9 17l-5-5",
    "x": "M6 6l12 12M18 6 6 18",
    "mic": rrect_path(9, 3, 6, 11, 3) + "M5 11a7 7 0 0 0 14 0M12 18v3",
    "search": circle_path(11, 11, 7) + "M20 20l-4.5-4.5",
    "finger": ("M10.6 11.8a1.4 1.4 0 0 1 2.8 0v3.6a8.6 8.6 0 0 1-.6 3.2"
               "M7.4 12a4.6 4.6 0 0 1 9.2 0v3a9.6 9.6 0 0 1-.6 3.3"
               "M4.2 12.2a7.8 7.8 0 0 1 15.6-.2v2.6a13 13 0 0 1-.7 4.2"
               "M4.3 15.4a12 12 0 0 0 1.6 4.4"
               "M5.2 7.4A8.4 8.4 0 0 1 17.6 5.9"),
    "card": rrect_path(2.5, 5, 19, 14, 2) + "M2.5 10h19M6.5 14.5h4",
    "arrow-left": "M19 12H5M11 18l-6-6 6-6",
    "plus": "M12 5v14M5 12h14",
    "bell": "M18 16V11a6 6 0 1 0-12 0v5l-1.5 2.5h15L18 16M10 21h4",
    "chevron-right": "M9 5l7 7-7 7",
}


def icon(parent, key, size=20, color=INK, weight=1.8, name=None):
    """An icon as a size x size frame holding one stroked vector."""
    d = ICONS[key]
    k = size / 24.0
    bx, by, bw, bh = bbox(d)
    box = create("frame", parent=parent, name=name or ("icon/" + key),
                 width=size, height=size,
                 props={"fill": "none", "clip": False, "layout": None})
    create("vector", parent=box, name=key, path=d,
           x=bx * (k - 1), y=by * (k - 1),
           width=max(bw * k, 0.01), height=max(bh * k, 0.01),
           props={"fill": "none", "stroke": color, "strokeWeight": 1.8 * k,
                  "strokeCap": "round", "strokeJoin": "round",
                  "constraints": {"horizontal": "scale", "vertical": "scale"}})
    return box


# --------------------------------------------------------------------------
# PayGo wordmark — the official logo artwork (PayGo-logo.svg, 122.45 x 39.11)
# --------------------------------------------------------------------------
LOGO_VB = (122.45, 39.11)
LOGO_PATHS = [
    ("P", LOGO_BLUE, "M19.47,13.32a7.85,7.85,0,0,1-3.35,3.19,12.25,12.25,0,0,1-5.75,1.21H5.65V28.54H0V.36H10.37a12.57,12.57,0,0,1,5.57,1.13A7.83,7.83,0,0,1,19.39,4.6a8.71,8.71,0,0,1,1.15,4.48A8.7,8.7,0,0,1,19.47,13.32ZM13.6,12.09a4,4,0,0,0,1.13-3C14.73,6.34,13.2,5,10.13,5H5.65v8.2h4.48A4.9,4.9,0,0,0,13.6,12.09Z"),
    ("a", LOGO_BLUE, "M22.82,11.26a10,10,0,0,1,3.68-4,9.75,9.75,0,0,1,5.18-1.41,9.14,9.14,0,0,1,4.38,1,8.87,8.87,0,0,1,3,2.54V6.17h5.69V28.54H39.07V25.27a8.45,8.45,0,0,1-3,2.6,9.16,9.16,0,0,1-4.42,1,9.55,9.55,0,0,1-5.14-1.45,10.19,10.19,0,0,1-3.68-4.1,13.18,13.18,0,0,1-1.35-6.08A13,13,0,0,1,22.82,11.26Zm15.44,2.56a5.72,5.72,0,0,0-2.18-2.26,5.83,5.83,0,0,0-2.94-.78,5.65,5.65,0,0,0-5.07,3,7.07,7.07,0,0,0-.83,3.49,7.29,7.29,0,0,0,.83,3.54,6,6,0,0,0,2.18,2.32,5.5,5.5,0,0,0,2.89.8,5.83,5.83,0,0,0,2.94-.78,5.72,5.72,0,0,0,2.18-2.26,7.22,7.22,0,0,0,.81-3.53A7.26,7.26,0,0,0,38.26,13.82Z"),
    ("y", LOGO_BLUE, "M70.07,6.17,56.22,39.11h-6L55.05,28l-9-21.8h6.34L58.2,21.79,64.05,6.17Z"),
    ("G", LOGO_GREEN, "M91.09,8.84a6.62,6.62,0,0,0-2.66-2.71,8.21,8.21,0,0,0-4-.92A8.56,8.56,0,0,0,80,6.34a7.7,7.7,0,0,0-3,3.23,10.28,10.28,0,0,0-1.09,4.84A10.38,10.38,0,0,0,77,19.33a7.83,7.83,0,0,0,3.09,3.23,9,9,0,0,0,4.6,1.13A8,8,0,0,0,90,22a8.17,8.17,0,0,0,2.71-4.79H83V12.88H98.28V17.8a13.42,13.42,0,0,1-2.42,5.45,13.7,13.7,0,0,1-4.79,4,14,14,0,0,1-6.56,1.52,14.78,14.78,0,0,1-7.36-1.84A13.36,13.36,0,0,1,72,21.83a14.61,14.61,0,0,1-1.87-7.42A14.8,14.8,0,0,1,72,7a13.32,13.32,0,0,1,5.17-5.12,15.72,15.72,0,0,1,15.6.48,11.87,11.87,0,0,1,4.84,6.52Z"),
    ("o", LOGO_GREEN, "M105,27.47a10.36,10.36,0,0,1-4.06-4.06,12.18,12.18,0,0,1-1.47-6.05A11.9,11.9,0,0,1,101,11.3a10.47,10.47,0,0,1,4.14-4.05,12.6,12.6,0,0,1,11.7,0,10.47,10.47,0,0,1,4.14,4.05,11.9,11.9,0,0,1,1.51,6.06,11.62,11.62,0,0,1-1.55,6.05,10.85,10.85,0,0,1-4.2,4.06,12.24,12.24,0,0,1-5.91,1.43A11.83,11.83,0,0,1,105,27.47Zm8.7-4.24A5.41,5.41,0,0,0,115.83,21a7.55,7.55,0,0,0,.81-3.63A6.78,6.78,0,0,0,115,12.45a5.52,5.52,0,0,0-4.1-1.71,5.4,5.4,0,0,0-4.06,1.71,6.85,6.85,0,0,0-1.63,4.91,7,7,0,0,0,1.59,4.9,5.28,5.28,0,0,0,4,1.71A5.89,5.89,0,0,0,113.68,23.23Z"),
]


def logo(parent, width=160, mono=None, name="PayGo logo"):
    """The PayGo wordmark. mono=<hex> renders every letter in one colour."""
    k = width / LOGO_VB[0]
    h = LOGO_VB[1] * k
    box = create("frame", parent=parent, name=name, width=width, height=h,
                 props={"fill": "none", "clip": False, "layout": None})
    for letter, color, d in LOGO_PATHS:
        bx, by, bw, bh = bbox(d)
        create("vector", parent=box, name=letter, path=d,
               x=bx * (k - 1), y=by * (k - 1),
               width=bw * k, height=bh * k,
               props={"fill": mono or color, "stroke": "none",
                      "constraints": {"horizontal": "scale", "vertical": "scale"}})
    return box


# --------------------------------------------------------------------------
# Layout primitives
# --------------------------------------------------------------------------
def col(parent=None, name="Group", gap=0, padding=0, fill="none", width=None,
        height=None, x=None, y=None, radius=None, stroke=None, stroke_weight=1,
        stroke_sides=None, main="min", cross="min", fill_w=False, hug_v=True,
        fill_v=False, clip=None, effects=None, fills=None):
    props = {"layout": "vertical", "gap": gap, "padding": padding,
             "mainAlign": main, "crossAlign": cross}
    props["fills"] = fills if fills else ([] if fill == "none" else [fill])
    if radius is not None:
        props["cornerRadius"] = radius
    if stroke:
        props["stroke"] = stroke
        props["strokeWeight"] = stroke_weight
        props["strokeAlign"] = "inside"
        if stroke_sides:
            props["strokeSides"] = stroke_sides
    if fill_w:
        props["sizingH"] = "fill"
    elif width is None:
        props["sizingH"] = "hug"
    if fill_v:
        props["sizingV"] = "fill"
    elif hug_v and height is None:
        props["sizingV"] = "hug"
    if clip is not None:
        props["clip"] = clip
    if effects:
        props["effects"] = effects
    return create("frame", parent=parent, name=name, x=x, y=y,
                  width=width, height=height, props=props)


def row(parent=None, name="Row", gap=8, padding=0, fill="none", width=None,
        height=None, x=None, y=None, radius=None, stroke=None, stroke_weight=1,
        stroke_sides=None, main="min", cross="center", fill_w=False, hug_v=True,
        fill_v=False, clip=None, effects=None, wrap=False, fills=None):
    props = {"layout": "horizontal", "gap": gap, "padding": padding,
             "mainAlign": main, "crossAlign": cross, "wrap": wrap}
    props["fills"] = fills if fills else ([] if fill == "none" else [fill])
    if radius is not None:
        props["cornerRadius"] = radius
    if stroke:
        props["stroke"] = stroke
        props["strokeWeight"] = stroke_weight
        props["strokeAlign"] = "inside"
        if stroke_sides:
            props["strokeSides"] = stroke_sides
    if fill_w:
        props["sizingH"] = "fill"
    elif width is None:
        props["sizingH"] = "hug"
    if fill_v:
        props["sizingV"] = "fill"
    elif hug_v and height is None:
        props["sizingV"] = "hug"
    if clip is not None:
        props["clip"] = clip
    if effects:
        props["effects"] = effects
    return create("frame", parent=parent, name=name, x=x, y=y,
                  width=width, height=height, props=props)


def spacer(parent, name="Spacer"):
    return create("frame", parent=parent, name=name, width=10, height=10,
                  props={"fills": [], "sizingH": "fill", "sizingV": "fill",
                         "layout": None})


def divider(parent, color=LINE, name="Divider"):
    return create("frame", parent=parent, name=name, width=100, height=1,
                  props={"fill": color, "sizingH": "fill", "layout": None})
