"""Build PARA bar chart + method flowchart as native Penpot shapes via update-file RPC.

Generates rect/text shapes (no images), so all elements are individually editable
in the Penpot UI: bars, labels, axes, boxes, arrows, text — every primitive.

Usage: python3 build_native_figures.py
"""

import json
import subprocess
import uuid

FILE_ID = "5688a1b5-92ba-8001-8007-dc4100064857"
PAGE_ID = "5688a1b5-92ba-8001-8007-dc4100064858"
ROOT = "00000000-0000-0000-0000-000000000000"
COOKIE = "/tmp/penpot_user_cookies.txt"

# ─────────────────────────────────────────────────────────────────────────────
# Shape constructors
# ─────────────────────────────────────────────────────────────────────────────

def _geom(x, y, w, h):
    """Common selrect/points/transform for axis-aligned shapes."""
    return {
        "x": x, "y": y, "width": w, "height": h, "rotation": 0,
        "selrect": {"x": x, "y": y, "x1": x, "y1": y, "x2": x + w, "y2": y + h,
                    "width": w, "height": h},
        "points": [{"x": x, "y": y}, {"x": x + w, "y": y},
                   {"x": x + w, "y": y + h}, {"x": x, "y": y + h}],
        "transform":        {"a": 1, "b": 0, "c": 0, "d": 1, "e": 0, "f": 0},
        "transformInverse": {"a": 1, "b": 0, "c": 0, "d": 1, "e": 0, "f": 0},
        "parentId": ROOT, "frameId": ROOT,
    }


def rect(name, x, y, w, h, fill=None, stroke=None, rx=0, sid=None):
    sid = sid or str(uuid.uuid4())
    obj = {"id": sid, "type": "rect", "name": name, **_geom(x, y, w, h)}
    if fill:
        obj["fills"] = [{"fillColor": fill, "fillOpacity": 1}]
    else:
        obj["fills"] = []
    if stroke:
        sw, sc = stroke
        obj["strokes"] = [{
            "strokeColor": sc, "strokeOpacity": 1, "strokeStyle": "solid",
            "strokeWidth": sw, "strokeAlignment": "center",
        }]
    if rx:
        obj["rx"] = rx
        obj["ry"] = rx
    return obj


def text(name, x, y, w, h, content, size=14, weight="400", color="#222222",
         align="left", sid=None):
    sid = sid or str(uuid.uuid4())
    obj = {
        "id": sid, "type": "text", "name": name, **_geom(x, y, w, h),
        "growType": "fixed",
        "content": {
            "type": "root",
            "children": [{
                "type": "paragraph-set",
                "children": [{
                    "type": "paragraph",
                    "textAlign": align,
                    "children": [{
                        "text": content,
                        "fontId": "sourcesanspro",
                        "fontFamily": "sourcesanspro",
                        "fontSize": str(size),
                        "fontWeight": weight,
                        "fills": [{"fillColor": color, "fillOpacity": 1}],
                    }],
                }],
            }],
        },
    }
    return obj


# ─────────────────────────────────────────────────────────────────────────────
# Bar chart layout
# ─────────────────────────────────────────────────────────────────────────────

def build_bar_chart(origin_x=600, origin_y=50):
    """Build the PARA vs ACT bar chart at the given origin (top-left)."""
    # Layout constants
    plot_x, plot_y = origin_x + 80, origin_y + 60
    plot_w, plot_h = 600, 400
    bar_group_w = plot_w / 3   # 3 task groups
    bar_w = 70
    bar_gap = 12

    PARA_COLOR = "#16653a"
    ACT_COLOR = "#a12029"

    tasks = ["Pick & Place", "Fold Towel", "Wipe Table"]
    para_vals = [97, 97, 95]
    act_vals = [9, 11, 0]

    shapes = []

    # Title
    shapes.append(text("title", origin_x + 60, origin_y + 10, plot_w + 40, 30,
                       "Real Robot Results: PARA vs ACT (20 demos)",
                       size=20, weight="700", align="center"))

    # Y-axis line
    shapes.append(rect("y-axis", plot_x - 1, plot_y, 2, plot_h, fill="#333333"))
    # X-axis line
    shapes.append(rect("x-axis", plot_x, plot_y + plot_h - 1, plot_w, 2, fill="#333333"))

    # Y-axis tick labels and gridlines (0, 25, 50, 75, 100)
    for tick_val in [0, 25, 50, 75, 100]:
        ty = plot_y + plot_h - (tick_val / 100) * plot_h
        # Gridline (skip 0 — it's the axis)
        if tick_val > 0:
            shapes.append(rect(f"grid-{tick_val}", plot_x, ty - 0.5, plot_w, 1,
                               fill="#dddddd"))
        # Tick label
        shapes.append(text(f"yt-{tick_val}", plot_x - 50, ty - 10, 40, 20,
                           str(tick_val), size=13, weight="400", align="right"))
    # Y-axis label
    shapes.append(text("y-label", origin_x + 5, plot_y + plot_h / 2 - 10, 70, 20,
                       "Success Rate (%)", size=13, weight="500", align="center"))

    # Bars and value labels
    for i, task in enumerate(tasks):
        group_center = plot_x + (i + 0.5) * bar_group_w
        # PARA bar
        para_h = (para_vals[i] / 100) * plot_h
        para_x = group_center - bar_w - bar_gap / 2
        shapes.append(rect(f"para-bar-{i}", para_x, plot_y + plot_h - para_h,
                           bar_w, para_h, fill=PARA_COLOR))
        # PARA value label
        shapes.append(text(f"para-val-{i}", para_x, plot_y + plot_h - para_h - 24,
                           bar_w, 20, f"{para_vals[i]}%", size=14, weight="700",
                           align="center"))
        # ACT bar
        act_h = (act_vals[i] / 100) * plot_h
        act_x = group_center + bar_gap / 2
        if act_vals[i] > 0:
            shapes.append(rect(f"act-bar-{i}", act_x, plot_y + plot_h - act_h,
                               bar_w, act_h, fill=ACT_COLOR))
        # ACT value label (always shown, even for 0%)
        shapes.append(text(f"act-val-{i}", act_x, plot_y + plot_h - act_h - 24,
                           bar_w, 20, f"{act_vals[i]}%", size=14, weight="700",
                           align="center"))
        # X-axis tick label
        shapes.append(text(f"xt-{i}", group_center - bar_group_w / 2,
                           plot_y + plot_h + 8, bar_group_w, 20,
                           task, size=14, weight="500", align="center"))

    # Legend (top-right corner)
    leg_x = plot_x + plot_w - 130
    leg_y = plot_y + 10
    shapes.append(rect("leg-para-swatch", leg_x, leg_y, 18, 18, fill=PARA_COLOR))
    shapes.append(text("leg-para", leg_x + 24, leg_y - 1, 90, 22, "PARA",
                       size=14, weight="600"))
    shapes.append(rect("leg-act-swatch", leg_x, leg_y + 26, 18, 18, fill=ACT_COLOR))
    shapes.append(text("leg-act", leg_x + 24, leg_y + 25, 90, 22, "ACT",
                       size=14, weight="600"))

    return shapes


# ─────────────────────────────────────────────────────────────────────────────
# Flowchart layout
# ─────────────────────────────────────────────────────────────────────────────

def build_flowchart(origin_x=600, origin_y=600):
    """Build the method flowchart at the given origin."""
    boxes = [
        ("RGB Image",         "",                      "#eaf2fb", "#3b6fa6"),
        ("DINO ViT-S/16",     "backbone",              "#fff3e6", "#d97e1f"),
        ("Per-Pixel Heatmap", "+ height bins",         "#fde9ec", "#c2415a"),
        ("3D Recovery",       "argmax + geometry",     "#e8f5ec", "#16653a"),
        ("Robot Action",      "EEF target + gripper",  "#efe6fb", "#5a3da2"),
    ]
    box_w, box_h = 170, 70
    box_gap = 30
    arrow_len = box_gap

    shapes = []

    # Title
    total_w = len(boxes) * box_w + (len(boxes) - 1) * box_gap
    shapes.append(text("flow-title", origin_x, origin_y, total_w, 30,
                       "PARA Method Pipeline", size=20, weight="700",
                       align="center"))

    box_y = origin_y + 50
    for i, (label, sub, fill, stroke_col) in enumerate(boxes):
        bx = origin_x + i * (box_w + box_gap)
        # Box
        shapes.append(rect(f"flow-box-{i}", bx, box_y, box_w, box_h,
                           fill=fill, stroke=(2, stroke_col), rx=14))
        # Main label
        if sub:
            shapes.append(text(f"flow-label-{i}", bx, box_y + 12, box_w, 22,
                               label, size=15, weight="600", align="center"))
            shapes.append(text(f"flow-sub-{i}", bx, box_y + 38, box_w, 18,
                               sub, size=11, weight="400", color="#5a5a5a",
                               align="center"))
        else:
            shapes.append(text(f"flow-label-{i}", bx, box_y + 24, box_w, 22,
                               label, size=15, weight="600", align="center"))

        # Arrow to next box (4 arrows total, 3 lines + arrowhead)
        if i < len(boxes) - 1:
            ax1 = bx + box_w
            ay = box_y + box_h / 2
            # Shaft
            shapes.append(rect(f"flow-arrow-shaft-{i}", ax1, ay - 1,
                               arrow_len - 8, 2, fill="#34495e"))
            # Arrowhead (small filled rect tilted? simplest: a small triangle-ish square)
            # Penpot doesn't have native polygons via add-obj easily; use a small rect
            shapes.append(rect(f"flow-arrow-head-{i}", ax1 + arrow_len - 10,
                               ay - 4, 8, 8, fill="#34495e"))

    # Caption
    shapes.append(text("flow-caption", origin_x, box_y + box_h + 30, total_w, 20,
                       "Image  →  Backbone features  →  Pixel-aligned heatmap  →  "
                       "Lift to 3D  →  Action",
                       size=12, weight="400", color="#5a5a5a", align="center"))

    return shapes


# ─────────────────────────────────────────────────────────────────────────────
# Main: build, send update-file
# ─────────────────────────────────────────────────────────────────────────────

def get_revn():
    r = subprocess.run([
        "curl", "-s", "-b", COOKIE,
        f"https://penpot.omidlab.net/api/rpc/command/get-file?id={FILE_ID}",
        "-H", "Accept: application/json",
    ], capture_output=True, text=True)
    d = json.loads(r.stdout)
    return d["revn"]


def main():
    bar_shapes = build_bar_chart(origin_x=400, origin_y=50)
    flow_shapes = build_flowchart(origin_x=400, origin_y=600)

    print(f"Bar chart: {len(bar_shapes)} shapes")
    print(f"Flowchart: {len(flow_shapes)} shapes")

    all_shapes = bar_shapes + flow_shapes
    changes = [
        {"type": "add-obj", "id": s["id"], "pageId": PAGE_ID,
         "frameId": ROOT, "parentId": ROOT, "obj": s}
        for s in all_shapes
    ]

    revn = get_revn()
    payload = {"id": FILE_ID, "sessionId": str(uuid.uuid4()),
               "revn": revn, "vern": 0, "changes": changes}

    print(f"Sending {len(changes)} add-obj changes (revn={revn})...")
    r = subprocess.run([
        "curl", "-s", "-b", COOKIE,
        "-X", "POST",
        "https://penpot.omidlab.net/api/rpc/command/update-file",
        "-H", "Content-Type: application/json",
        "-H", "Accept: application/json",
        "-d", json.dumps(payload),
    ], capture_output=True, text=True)
    out = r.stdout[:600]
    print("Response:", out)


if __name__ == "__main__":
    main()
