#!/usr/bin/env python3
from __future__ import annotations
import hashlib, json, sys
from pathlib import Path

def sha256_file(path: Path) -> str:
    h = hashlib.sha256()
    with path.open('rb') as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b''):
            h.update(chunk)
    return h.hexdigest()

def merkle_root(hex_hashes):
    hs = [bytes.fromhex(h) for h in sorted(hex_hashes)]
    if not hs:
        return hashlib.sha256(b'').hexdigest()
    while len(hs) > 1:
        nxt = []
        for i in range(0, len(hs), 2):
            a = hs[i]
            b = hs[i+1] if i + 1 < len(hs) else a
            nxt.append(hashlib.sha256(a + b).digest())
        hs = nxt
    return hs[0].hex()

def main(argv):
    if len(argv) != 2:
        print('usage: python verify_bounda_pack.py <unzipped-pack-directory>')
        return 2
    root = Path(argv[1]).resolve()
    manifest_path = root / 'manifest.json'
    if not manifest_path.exists():
        print('manifest: MISSING')
        return 1
    manifest = json.loads(manifest_path.read_text(encoding='utf-8'))
    ok = True
    observed = []
    for item in manifest.get('files', []):
        p = root / item['path']
        if not p.exists():
            print(f"sha256: MISSING {item['path']}")
            ok = False
            continue
        d = sha256_file(p)
        observed.append(d)
        if d != item.get('sha256'):
            print(f"sha256: FAIL {item['path']}")
            ok = False
        else:
            print(f"sha256: OK {item['path']}")
    mr = merkle_root(observed)
    if mr != manifest.get('merkle_root'):
        print('merkle_root: FAIL')
        print(' expected:', manifest.get('merkle_root'))
        print(' observed:', mr)
        ok = False
    else:
        print('merkle_root: OK')
    print('manifest: OK')
    print('EXTERNAL VERIFY:', 'PASS' if ok else 'FAIL')
    return 0 if ok else 1

if __name__ == '__main__':
    raise SystemExit(main(sys.argv))
