"""Verify root facts from the user's installed base models, not guessed indices.
Optional: --source-dir directory containing the nine frozen *.unit-main.bin files.
Without it, verify the packaged metadata; with it, independently parse full main data.
No process access, game writes, automatic downloads, or GPU payloads.
"""
from pathlib import Path
import sys,json,struct,hashlib,argparse
R=Path(__file__).resolve().parents[1];sys.path.insert(0,str(R/'tools'))
from archive_writer import murmur64
p=argparse.ArgumentParser();p.add_argument('--source-dir',type=Path);args=p.parse_args()
s=json.loads((R/'validation/model-node-schema.json').read_text());resource=json.loads((R/'validation/resource-schema.json').read_text())
name=s['root_name'];name_hash=f'{murmur64(name.encode())>>32:08x}';assert name=='StingrayEntityRoot'and name_hash==s['root_name_hash']=='4a182741'
models=s['models'];assert len(models)==9
assert {m['id']for m in models}=={rows[0]['engine_unit_resource']for rows in resource['mappings'].values()}
total=0
for m in models:
 n=m['joint_count'];raw=bytes.fromhex(m['joint_map_hex']);assert len(raw)==8*n
 parents=[struct.unpack_from('<HH',raw,4*i)[1]for i in range(n)];hashes=[f'{struct.unpack_from("<I",raw,4*n+4*i)[0]:08x}'for i in range(n)]
 roots=[i for i,parent in enumerate(parents)if i==parent];assert len(roots)==1
 root=roots[0];assert hashes[root]==name_hash and hashes.count(name_hash)==1
 assert len(m['root_candidates'])==1 and m['root_candidates'][0]['index']==root and m['root_candidates'][0]['names']==[name]
 for i in range(n):
  seen=set();j=i
  while j!=parents[j]:
   assert j not in seen and 0<=parents[j]<n;seen.add(j);j=parents[j]
  assert j==root
 if args.source_dir:
  b=(args.source_dir/(m['id']+'.unit-main.bin')).read_bytes();assert len(b)==m['mainSize'] and hashlib.sha256(b).hexdigest()==m['sha256']
  assert b[:116].hex()==m['header_hex'];jo=struct.unpack_from('<I',b,52)[0];assert jo==m['joint_offset']
  count=struct.unpack_from('<I',b,jo)[0];assert count==n and jo+16+136*n<=len(b)
  start=jo+16+128*n;assert b[start:start+8*n]==raw
  assert hashlib.sha256(b[jo:jo+16+136*n]).hexdigest()==m['joint_table_sha256']
  for r in m['root_candidates']:
   xyz=struct.unpack_from('<3f',b,jo+16+64*r['index']+36);assert list(xyz)==r['local_position']==[0,0,0]
 total+=n
bridge=(R/'src/unit_ref_bridge.lua').read_text().split('function M.root_node(u,d)',1)[1].split('return M',1)[0]
assert "local name='StingrayEntityRoot'"in bridge and 'node(u,name)'in bridge and 'return i,name'in bridge
assert 'return 0,'not in bridge and 'return 1,'not in bridge
print(json.dumps({'installed_base_models':len(models),'joint_records_checked':total,'canonical_root_name':name,'unique_root_in_every_model':True,'all_parent_chains_reach_root':True,'runtime_index_from_Unit_node':True,'full_model_bytes_independently_verified':bool(args.source_dir),'active_patch_overrides_enumerated':False,'live_root_API_verified':False,'multiplayer_UI_verified':False}))
