# SPDX-License-Identifier: Apache-2.0
"""Input stamps for the generated CAD files, so a stale step shows up as a failed check, not as wrong numbers.

Each pipeline step calls write(step) when it finishes. That records a hash of each file the step reads,
in out_pebble/stamps.json. stale() compares them with the files as they are now: validate.py fails on any
difference, and v01/build.py reruns only the steps that are stale.
"""
from __future__ import annotations

import hashlib
import json
import time
from pathlib import Path

CAD = Path(__file__).parent
OUT = CAD / "out_pebble"
STAMPS = OUT / "stamps.json"

# what each step reads, in pipeline order (paths relative to v01/cad)
STEPS = {
    "enclosure": ["pebble_enclosure.py", "pebble_layout.py", "pebble.py", "stages.py", "pebble_spec.json"],
    "wires": ["wire_routes.py", "board_pads.py", "pebble_layout.py", "pebble.py", "pebble_spec.json", "out_pebble/layout.json"],
    "web": ["make_web.py", "print_spec.json", "out_pebble/layout.json", "out_pebble/*.stl"],
}
SCRIPTS = {"enclosure": "pebble_enclosure.py", "wires": "wire_routes.py", "web": "make_web.py"}


def _files(step):
    out = []
    for pat in STEPS[step]:
        out += sorted(CAD.glob(pat)) if "*" in pat else [CAD / pat]
    return out


def digest(step):
    return {str(p.relative_to(CAD)): hashlib.sha256(p.read_bytes()).hexdigest()[:16] for p in _files(step) if p.exists()}


def write(step, **extra):
    s = json.loads(STAMPS.read_text()) if STAMPS.exists() else {}
    s[step] = {"at": time.strftime("%Y-%m-%d %H:%M:%S"), "inputs": digest(step), **extra}
    STAMPS.write_text(json.dumps(s, indent=1))


def stale():
    """{step: [changed inputs]} for every step whose inputs differ from its last run (or that never ran)."""
    s = json.loads(STAMPS.read_text()) if STAMPS.exists() else {}
    out = {}
    for step in STEPS:
        if step not in s:
            out[step] = ["never stamped"]
            continue
        now, then = digest(step), s[step]["inputs"]
        diff = sorted(k for k in set(now) | set(then) if now.get(k) != then.get(k))
        if diff:
            out[step] = diff
    return out
