"""Relink an isolated native-menu core from the pinned, hardened local build.

Only the host is recompiled. Every reused source and object is inventoried;
the original core, ROM and reconstruction source tree remain unchanged.
"""
import argparse
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess

HERE = Path(__file__).resolve().parent
PLAYER = HERE.parent


def digest(path, algorithm="sha256"):
    return hashlib.new(algorithm, path.read_bytes()).hexdigest()


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--workspace", type=Path, default=Path("C:/Dev/decompile-snes"))
    parser.add_argument("--rom", type=Path, required=True)
    args = parser.parse_args()
    workspace, rom = args.workspace.resolve(), args.rom.resolve()
    baseline = workspace / "external/mesen-platform-probe/probe-build/browser-restore-rebuild-20260921"
    inputs = json.loads((baseline / "source-inputs.json").read_text())
    if inputs["pin"] != "9e4fdeb9b336470bc96beb8765b2e79c86a2da1e" or not inputs["restore_candidate"]:
        raise RuntimeError("Expected the pinned hardened browser build")
    for source in inputs["sources"][:-1]:
        if digest(workspace / source["path"]) != source["sha256"]:
            raise RuntimeError("Cached build source changed: " + source["path"])
    if rom.stat().st_size != 524288:
        raise RuntimeError("Native experiment expects a 512 KiB LoROM")
    rom_hash = digest(rom)
    snapshot_dir = rom.parent.parent / ("native-browser-" + rom_hash[:16])
    snapshot_dir.mkdir(parents=True, exist_ok=True)
    snapshot = snapshot_dir / "game.sfc"
    if not snapshot.exists():
        shutil.copyfile(rom, snapshot)
    if digest(snapshot) != rom_hash:
        raise RuntimeError("The immutable native ROM snapshot does not match")
    rom = snapshot
    # A host change also gets a new directory, preserving running previews.
    build_key = rom_hash[:16] + "-" + digest(HERE / "sfc-host.cpp")[:12]
    output = PLAYER / "external/native-core" / build_key
    output.mkdir(parents=True, exist_ok=True)
    commands = json.loads((baseline / "commands.json").read_text())
    compile_command, link_command = commands[-2].copy(), commands[-1].copy()
    old_host = compile_command[compile_command.index("-o") + 1]
    new_host = str(output / "native-host.o")
    compile_command[compile_command.index("-c") + 1] = str(HERE / "sfc-host.cpp")
    compile_command[compile_command.index("-o") + 1] = new_host
    compile_command += ["-DMESEN_NATIVE_MENU", '-DMESEN_NATIVE_ROM_SHA1="' + digest(rom, "sha1").upper() + '"']
    link_command = [new_host if flag == old_host else
                    flag + ",_sfc_native_read,_sfc_native_write" if flag.startswith("-sEXPORTED_FUNCTIONS=") else flag
                    for flag in link_command]
    link_command[link_command.index("-o") + 1] = str(output / "sfc.js")
    objects = {flag: digest(Path(flag)) for flag in link_command if flag.endswith(".o") and flag != new_host}
    env = dict(os.environ)
    env["EM_CONFIG"] = str(workspace / "external/ares-platform-probe/probe-build/emscripten-config.py")
    for label, command in (("compile", compile_command), ("link", link_command)):
        result = subprocess.run(command, env=env, capture_output=True, text=True, timeout=300)
        (output / (label + ".log")).write_text(result.stdout + result.stderr, encoding="utf-8")
        if result.returncode:
            raise RuntimeError(label + " failed: " + result.stderr[-4000:])
        print(label + " passed", flush=True)
    manifest = {
        "path": "/private/native-game.sfc", "romFile": str(rom), "sha256": digest(rom),
        "coreDir": str(output), "corePath": "/native-core/",
        "coreBuildSha256": digest(output / "sfc.wasm"), "loaderSha256": digest(output / "sfc.js"),
        "mailbox": {"offset": 0x1ff00, "length": 256, "version": 1},
    }
    (PLAYER / "external/native-game.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
    (output / "build-inputs.json").write_text(json.dumps({"cachedBuild": str(baseline), "objects": objects,
        "hostSha256": digest(HERE / "sfc-host.cpp"), "commands": [compile_command, link_command]}, indent=2), encoding="utf-8")
    print(json.dumps(manifest), flush=True)


if __name__ == "__main__":
    main()
