#!/usr/bin/env python3
"""
Verify ELF symbols against firmware exports. Rerunnable from skill.

Usage:
  python3 scripts/verify_symbols.py [--elf Apps/BibleVerse/build/cmake-build-esp32s3/BibleVerse.app.elf] [--firmware ../firmware]

Requires: xtensa-esp32s3-elf-nm in PATH after sourcing ~/esp/esp-idf/export.sh
"""
import argparse, pathlib, re, subprocess, sys

def get_undef(elf_path: pathlib.Path):
    r = subprocess.run(
        ["zsh","-c", f"source /Users/adolforeyna/esp/esp-idf/export.sh >/dev/null 2>&1; xtensa-esp32s3-elf-nm -D {elf_path}"],
        capture_output=True, text=True
    )
    undef=[]
    for line in r.stdout.splitlines():
        parts=line.split()
        if len(parts)>=2 and parts[-2]=='U':
            undef.append(parts[-1])
    return undef, r.stdout

def get_exports(firmware_dir: pathlib.Path):
    pat=re.compile(r'(?:ESP_ELFSYM_EXPORT|DEFINE_MODULE_SYMBOL)\s*\(\s*([A-Za-z0-9_]+)\s*\)')
    exp=set()
    for fp in firmware_dir.rglob("*.*"):
        if 'build' in str(fp): continue
        if fp.suffix not in ('.c','.h','.cpp'): continue
        try:
            exp.update(m.group(1) for m in pat.finditer(fp.read_text(errors='ignore')))
        except: pass
    return exp

def main():
    p=argparse.ArgumentParser()
    p.add_argument('--elf', default='Apps/BibleVerse/build/cmake-build-esp32s3/BibleVerse.app.elf')
    p.add_argument('--firmware', default='../firmware')
    args=p.parse_args()
    elf=pathlib.Path(args.elf)
    fw=pathlib.Path(args.firmware)
    if not elf.exists():
        print(f"ELF not found: {elf}", file=sys.stderr)
        sys.exit(2)
    undef, raw = get_undef(elf)
    exp = get_exports(fw)
    missing=[s for s in undef if s not in exp]
    print(f"Undefined: {len(undef)}")
    print(f"Exported: {len(exp)}")
    if missing:
        print(f"Missing {len(missing)}:")
        for s in sorted(missing):
            print(f"  - {s}")
        sys.exit(1)
    print("✅ All symbols resolved")
    sys.exit(0)

if __name__ == '__main__':
    main()
