#!/usr/bin/env python3
"""Independent re-derivation of every number in raw-measurements.json.

Re-runs the same engine command and the same potrace command for all three
inputs (regenerating the synthetic PNG deterministically), then compares the
deterministic fields: output bytes, sha256, M-command count, path-element
count and distinct fill count. Wall time is NOT compared - it varies with
load - but the re-run times are recorded for context.

Writes recheck.json next to this script. Artifacts go to a temp dir that is
deleted afterwards.

Usage: venv313/bin/python verify_recheck.py
"""
from __future__ import annotations

import hashlib
import json
import os
import shutil
import subprocess
import sys
import tempfile
import time

MEAS_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, MEAS_DIR)

import measure  # noqa: E402  (shared helpers: svg_stats, describe_input, make_synthetic)

RAW = os.path.join(MEAS_DIR, "raw-measurements.json")
OUT = os.path.join(MEAS_DIR, "recheck.json")

COMPARED_FIELDS = ["bytes", "sha256", "m_commands", "path_elements",
                   "distinct_fill_or_stroke_values", "distinct_colors",
                   "image_elements"]
# Inkscape records the document name/path inside the SVG, which changes with
# the temp dir, so only the geometry/colour counts are compared for it.
INKSCAPE_FIELDS = ["m_commands", "path_elements", "distinct_colors",
                   "image_elements"]


def sha256(path: str) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as fh:
        for chunk in iter(lambda: fh.read(1 << 16), b""):
            h.update(chunk)
    return h.hexdigest()


def main() -> int:
    raw = json.load(open(RAW, encoding="utf-8"))
    rundir = tempfile.mkdtemp(prefix="any2svg-recheck-")
    results = {"generated_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
               "raw_file": RAW, "checks": []}

    for entry in raw["runs"]:
        name = os.path.basename(entry["input"]["path"])
        name = os.path.splitext(name)[0]
        if name == "synthetic-flat":
            src = measure.make_synthetic(rundir)
        else:
            src = entry["input"]["path"]

        input_sha_now = sha256(src)
        results["checks"].append({
            "kind": "input",
            "name": name,
            "sha256_matches": input_sha_now == entry["input"]["sha256"],
            "sha256_now": input_sha_now,
        })

        for m in entry["measurements"]:
            if "error" in m:
                continue
            fields = COMPARED_FIELDS
            if m["tool"].startswith("any2svg engine"):
                outdir = os.path.join(rundir, "engine", name)
                os.makedirs(outdir, exist_ok=True)
                cmd = [sys.executable, measure.ENGINE, "--input", src,
                       "--outdir", outdir, "--preset", "balanced",
                       "--mode", "auto"]
                t0 = time.perf_counter()
                proc = subprocess.run(cmd, capture_output=True, text=True,
                                      timeout=900)
                wall = time.perf_counter() - t0
                manifest = json.loads(proc.stdout.strip().splitlines()[-1])
                now = measure.svg_stats(manifest["svg"])
            elif m["tool"].startswith("inkscape"):
                fields = INKSCAPE_FIELDS
                outdir = os.path.join(rundir, "inkscape")
                os.makedirs(outdir, exist_ok=True)
                svg = os.path.join(outdir, name + ".svg")
                actions = (f"select-all;object-trace:8,1,0,0,2,1,1;"
                           f"export-filename:{svg};export-do")
                t0 = time.perf_counter()
                proc = subprocess.run(
                    ["/opt/homebrew/bin/inkscape", f"--actions={actions}", src],
                    capture_output=True, text=True, timeout=900)
                wall = time.perf_counter() - t0
                now = measure.svg_stats(svg)
            else:
                prep = os.path.join(rundir, "potrace")
                os.makedirs(prep, exist_ok=True)
                pbm = os.path.join(prep, name + ".pbm")
                svg = os.path.join(prep, name + ".svg")
                from PIL import Image
                Image.open(src).convert("L").point(
                    lambda v: 255 if v >= 128 else 0, mode="1").save(pbm)
                t0 = time.perf_counter()
                proc = subprocess.run(
                    ["potrace", "-s", "-o", svg, pbm],
                    capture_output=True, text=True, timeout=900)
                wall = time.perf_counter() - t0
                now = measure.svg_stats(svg)

            expected = m["output"]
            diffs = {f: {"expected": expected[f], "now": now[f]}
                     for f in fields if expected[f] != now[f]}
            results["checks"].append({
                "kind": m["tool"],
                "name": name,
                "exit_code": proc.returncode,
                "first_run_wall_seconds": m["wall_seconds"],
                "recheck_wall_seconds": round(wall, 3),
                "fields_compared": fields,
                "mismatches": diffs,
                "pass": not diffs and proc.returncode == 0,
            })
            print(f"{name} / {m['tool'][:20]}: "
                  f"{'PASS' if not diffs else 'FAIL ' + str(diffs)}", flush=True)

    shutil.rmtree(rundir, ignore_errors=True)
    results["all_pass"] = all(
        c.get("pass", c.get("sha256_matches", True)) for c in results["checks"])
    with open(OUT, "w", encoding="utf-8") as fh:
        json.dump(results, fh, indent=2)
        fh.write("\n")
    print("all_pass =", results["all_pass"], "->", OUT)
    return 0 if results["all_pass"] else 1


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