#!/usr/bin/env python3
import hashlib
import json
import pathlib
import re
import subprocess

ROOT = pathlib.Path(__file__).resolve().parent
host = subprocess.run(["sh", "build_host_tests.sh"], cwd=ROOT, check=True,
                      capture_output=True, text=True)
subprocess.run(["sh", "build_target_diagnostic.sh"], cwd=ROOT, check=True,
               capture_output=True, text=True)
sanitized_binary = ROOT / "build/drill_smo_sanitized"
subprocess.run(["cc", "-std=c11", "-O1", "-g", "-Wall", "-Wextra", "-Werror",
                "-pedantic", "-fsanitize=address,undefined", "drill_smo.c",
                "test_drill_smo.c", "-lm", "-o", str(sanitized_binary)],
               cwd=ROOT, check=True, capture_output=True, text=True)
sanitizer = subprocess.run([str(sanitized_binary)], cwd=ROOT, check=True,
                           capture_output=True, text=True)
if "DRILL_SMO_HOST_TEST_PASS" not in sanitizer.stdout:
    raise SystemExit("ASan/UBSan host test evidence missing")
(ROOT / "sanitizer_test.log").write_text(sanitizer.stdout)
host_log = (ROOT / "host_test.log").read_text()
target = json.loads((ROOT / "build/build_verification.json").read_text())
forward = re.search(r"FORWARD synthetic: ([\-0-9.]+) rpm, angle error ([\-0-9.]+) rad, locked=(\d)", host_log)
reverse = re.search(r"REVERSE synthetic: ([\-0-9.]+) rpm, angle error ([\-0-9.]+) rad, locked=(\d)", host_log)
if "DRILL_SMO_HOST_TEST_PASS" not in host_log or not forward or not reverse:
    raise SystemExit("host synthetic test evidence missing")
f_rpm, f_error, f_locked = float(forward[1]), float(forward[2]), int(forward[3])
r_rpm, r_error, r_locked = float(reverse[1]), float(reverse[2]), int(reverse[3])
if not (47000.0 <= f_rpm <= 51000.0 and abs(f_error) < 0.20 and f_locked == 1):
    raise SystemExit("forward synthetic tracking outside test bounds")
if not (-48000.0 <= r_rpm <= -42000.0 and abs(r_error) < 0.20 and r_locked == 1):
    raise SystemExit("reverse synthetic tracking outside test bounds")
if target.get("status") != "LINKED_TARGET_DIAGNOSTIC_STATIC_CHECK_PASS":
    raise SystemExit("target image verifier did not pass")
if target.get("TIM1_ADC_SPI_initialized") or target.get("DRV_RUN_REQ_or_MOE_enabled"):
    raise SystemExit("diagnostic image must keep motor outputs and peripherals disabled")
result = {
    "status": "SYNTHETIC_HOST_AND_TARGET_LINK_PASS_PHYSICAL_NOT_RUN",
    "host": {
        "status": "PASS",
        "forward_rpm": f_rpm,
        "forward_angle_error_rad": f_error,
        "reverse_rpm": r_rpm,
        "reverse_angle_error_rad": r_error,
        "checks": ["svpwm_voltage_reconstruction", "cordic_range_and_invalid_input",
                   "forward_reverse_bemf_tracking", "no_bemf_unlock",
                   "current_and_configuration_rejection"],
        "sanitizer_run": "ASAN_UBSAN_PASS",
    },
    "target": target,
    "physical_motor_test": "NOT_RUN_NO_SAMPLE",
    "product_firmware_release": False,
}
(ROOT / "verification.json").write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n")
manifest = [
    "README.md", "drill_smo.c", "drill_smo.h", "test_drill_smo.c",
    "target_diag_main.c", "build_host_tests.sh", "build_target_diagnostic.sh",
    "verify_target_image.py", "verify_candidate.py", "host_test.log",
    "verification.json", "sanitizer_test.log", "build/build_verification.json",
    "build/drill_smo_target_diagnostic.bin", "build/drill_smo_target_diagnostic.elf",
    "build/drill_smo_target_diagnostic.map", "build/size.txt",
]
lines = []
for relative in manifest:
    path = ROOT / relative
    if not path.is_file():
        raise SystemExit("manifest input missing: " + relative)
    lines.append(f"{hashlib.sha256(path.read_bytes()).hexdigest()}  {relative}\n")
(ROOT / "MANIFEST-SHA256.txt").write_text("".join(lines))
print(json.dumps({"status": result["status"], "manifest_entries": len(lines),
                  "forward_rpm": f_rpm, "reverse_rpm": r_rpm,
                  "target_flash_bytes": target["flash_bytes"],
                  "target_ram_bytes": target["ram_bytes"]}, ensure_ascii=False, indent=2))
