"""Apply a revision: rebuild a design under the SAME name and resolve notes.

The agent translates the customer's note into an updated spec (that's
judgment); this tool does the mechanics atomically:

  .cad-venv/bin/python harness/revise.py <name> --spec-json '<updated spec>' \
      [--resolve <note-id> ...] [--note "what changed"]

Same name -> same studio link: /viewer/design.html?name=<name> just updates.
If the design had a production build, it is invalidated (the customer
re-approves the revised concept).
"""

from __future__ import annotations

import argparse
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
PYBIN = str(ROOT / ".cad-venv" / "bin" / "python")
COMMENTS = ROOT / "artifacts" / "review" / "comments.json"
JOBS = ROOT / "artifacts" / "review" / "jobs.json"


def _set_revision(name: str, status: str, **extra) -> None:
    jobs = json.loads(JOBS.read_text()) if JOBS.exists() else {}
    prev = jobs.get(f"revision-{name}", {})
    now = datetime.now(timezone.utc).isoformat(timespec="seconds")
    jobs[f"revision-{name}"] = {
        "status": status, **extra,
        "started": prev.get("started", now) if status != "designing" else now,
        "updated": now}
    JOBS.parent.mkdir(parents=True, exist_ok=True)
    JOBS.write_text(json.dumps(jobs, indent=2))


def main() -> int:
    p = argparse.ArgumentParser()
    p.add_argument("name")
    p.add_argument("--spec-json", required=True)
    p.add_argument("--resolve", nargs="*", default=[])
    p.add_argument("--note", default="")
    p.add_argument("--full", action="store_true",
                   help="synchronous gated build (default: fast previews + "
                        "detached gated build)")
    args = p.parse_args()

    spec = json.loads(args.spec_json)
    if spec.get("name") != args.name:
        print(f"error: spec.name {spec.get('name')!r} != {args.name!r} — a "
              "revision keeps the same name (same studio link)")
        return 2

    if args.full:
        r = subprocess.run([PYBIN, "-m", "ringcli.cli", "build", "--spec-json",
                            json.dumps(spec)], cwd=ROOT, capture_output=True,
                           text=True, timeout=1800)
        out = r.stdout.strip()
        result = json.loads(out[out.index("{"):]) if "{" in out else {
            "ok": False, "errors": [out[-300:] or r.stderr[-300:]]}
        if not result.get("ok"):
            print(json.dumps({"ok": False, "errors": result.get("errors")},
                             indent=1))
            return 1
    else:
        # FAST path (~30s): generator + colored GLB + renders now; the gated
        # STEP/STL build runs detached and reopens a note if it fails
        _set_revision(args.name, "designing")
        sys.path.insert(0, str(ROOT))
        from ringcli.cli import write_generator
        write_generator(spec)
        gen = ROOT / "designs" / f"{args.name}.py"
        outdir = ROOT / "artifacts" / "cli" / args.name
        outdir.mkdir(parents=True, exist_ok=True)
        subprocess.run([PYBIN, "-m", "ringcli.glb", str(gen),
                        str(outdir / f"{args.name}.parts.glb")],
                       cwd=ROOT, capture_output=True, timeout=300)
        for extra in ([str(outdir / f"{args.name}.png"), "25:16"],
                      [str(outdir / f"{args.name}-head.png"), "25:16", "--head"]):
            subprocess.run([PYBIN, "-m", "ringcli.render", str(gen)] + extra,
                           cwd=ROOT, capture_output=True, timeout=300)
        _set_revision(args.name, "preview_ready")
        subprocess.Popen([PYBIN, str(ROOT / "harness" / "fullbuild.py"),
                          args.name, json.dumps(spec)], cwd=ROOT,
                         stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
                         start_new_session=True)

    now = datetime.now(timezone.utc).isoformat(timespec="seconds")
    if args.resolve and COMMENTS.exists():
        data = json.loads(COMMENTS.read_text())
        for c in data["comments"]:
            if c["id"] in args.resolve:
                c["status"] = "addressed"
                c["resolved"] = now
                if args.note:
                    c["note"] = args.note
        COMMENTS.write_text(json.dumps(data, indent=2))

    # a revised concept invalidates any prior production build
    if JOBS.exists():
        jobs = json.loads(JOBS.read_text())
        if args.name in jobs:
            jobs.pop(args.name)
            JOBS.write_text(json.dumps(jobs, indent=2))

    print(json.dumps({"ok": True, "name": args.name,
                      "resolved": args.resolve,
                      "link": f"/viewer/design.html?name={args.name}"}, indent=1))
    return 0


if __name__ == "__main__":
    sys.exit(main())
