#!/usr/bin/env python3
"""Cross-check component YAML against a fresh `node_dump.js` JSON.

Proves the YAML still matches Figma exactly, value by value. It catches
hand edits and design changes made since harvesting:

    python3 tools/figma-spec/crosscheck.py SPEC_DIR FRESH_DUMP.json [--write]

For every component YAML whose node id is in the fresh dump, the stored
`design` + `children` are compared with the fresh node. Differences are
printed as `path: yaml=<v> figma=<v>`. With --write, each YAML's
`verification.values_crosscheck` is set to pass/fail and the mismatch list is
recorded. Exit code 1 when any component mismatches.
"""

import argparse
import glob
import json
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import yaml_io  # noqa: E402

IGNORED = set()
TOLERANCE = 1e-9


def diff(a, b, path, out):
    if isinstance(a, dict) and isinstance(b, dict):
        for key in sorted(set(a) | set(b)):
            if key in IGNORED:
                continue
            field = f"{path}.{key}" if path else key
            if key not in a or key not in b:
                out.append((field, a.get(key, "<missing>"), b.get(key, "<missing>")))
            else:
                diff(a[key], b[key], field, out)
    elif isinstance(a, list) and isinstance(b, list):
        if len(a) != len(b):
            out.append((path + ".length", len(a), len(b)))
        for i, (x, y) in enumerate(zip(a, b)):
            diff(x, y, f"{path}[{i}]", out)
    elif isinstance(a, (int, float)) and isinstance(b, (int, float)) and not isinstance(a, bool) and not isinstance(b, bool):
        if abs(a - b) > TOLERANCE:
            out.append((path, a, b))
    elif (isinstance(a, bool) != isinstance(b, bool)) or a != b:
        out.append((path, a, b))


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("spec_dir")
    ap.add_argument("fresh_dump")
    ap.add_argument("--write", action="store_true")
    args = ap.parse_args()

    with open(args.fresh_dump, encoding="utf-8") as fh:
        raw = json.load(fh)
    if isinstance(raw, str):
        raw = json.loads(raw)
    nodes = raw.get("nodes", [])
    if len({n["id"] for n in nodes}) != len(nodes):
        raise ValueError("duplicate IDs in fresh dump")
    fresh = {n["id"]: n for n in nodes}

    failed = len(raw.get("missing", []))
    for node_id in raw.get("missing", []):
        print(f"FAIL fresh dump reports missing node {node_id}")
    checked = 0
    for path in sorted(glob.glob(os.path.join(args.spec_dir, "components", "*.yml"))):
        doc = yaml_io.load(path) or {}
        comp = doc.get("component") or {}
        node_id = comp.get("node_id")
        if not node_id or node_id not in fresh:
            failed += 1
            print(f"FAIL {path}: node absent from fresh dump")
            if args.write:
                doc.setdefault("verification", {})["values_crosscheck"] = "unverified"
                yaml_io.dump(doc, path)
            continue
        checked += 1
        node = fresh[node_id]
        stored = dict(doc.get("design") or {})
        stored["children"] = doc.get("children") or []
        live = {k: v for k, v in node.items() if k not in ("id", "name", "type", "x", "y")}
        live.setdefault("children", [])
        mismatches = []
        diff({"width": doc.get("frame", {}).get("width"), "height": doc.get("frame", {}).get("height")},
             {"width": node.get("width"), "height": node.get("height")}, "frame", mismatches)
        diff(stored, live, "", mismatches)
        status = "pass" if not mismatches else "fail"
        if mismatches:
            failed += 1
            print(f"FAIL {comp.get('name')} ({node_id}) — {len(mismatches)} difference(s)")
            for p, a, b in mismatches[:25]:
                print(f"    {p}: yaml={a!r} figma={b!r}")
        else:
            print(f"PASS {comp.get('name')} ({node_id})")
        if args.write:
            ver = doc.setdefault("verification", {})
            ver["values_crosscheck"] = status
            ver["values_checked_at"] = raw.get("dumpedAt")
            ver["mismatches"] = [{"path": p, "yaml": a, "figma": b} for p, a, b in mismatches[:50]]
            yaml_io.dump(doc, path, header=f"Generated by tools/figma-spec/dump_to_yaml.py from Figma node {node_id}.\nDo not hand-edit design values; harvest again instead.")

    print(f"\n{checked} component(s) checked, {failed} mismatched")
    sys.exit(1 if failed or not checked else 0)


if __name__ == "__main__":
    main()
