"""Promote checked contact-corrected GLBs; write the exact requested catalog schema.
No Factory, Engine, game or service files are touched.
"""
from pathlib import Path
import json,shutil,hashlib
P=Path('/opt/creature-lib-20261017');E=P/'evidence';backup=P/'sources/pre-contact';backup.mkdir(exist_ok=True)
for f in sorted((P/'candidates').glob('*.glb')):
 ident=f.stem;r=json.loads((E/(ident+'-footplant-runtime.json')).read_text())
 assert not r['errors'] and not r['browser_errors'] and r['skins']==1 and r['maxWeights']<=4
 assert 3000<=r['triangles']<=20000 and f.stat().st_size<=1500000
 dst=P/'glb'/f.name
 if not (backup/f.name).exists():shutil.copy2(dst,backup/f.name)
 shutil.copy2(f,dst)
 # Evidence entries for experiments stay out of the main catalog.
 (E/(ident+'-footplant-entry.json')).rename(E/(ident+'-footplant-entry.archived.json'))
# Honest fantasy labels: the source 'Goleling' is a winged goblin head, not a stone golem.
for before,after in [('floating-golem','flying-goblin'),('flying-squid','winged-imp'),('pigeon','flying-bird')]:
 old='enemy-'+before;new='enemy-'+after
 for tail in ['-entry.json','-normalization.json']:
  src=E/(old+tail)
  if src.exists():
   d=json.loads(src.read_text())
   if tail=='-entry.json':d.update(id=new,file=new+'.glb',names=[after],good_for=[after,'fantasy','sky'])
   (E/(new+tail)).write_text(json.dumps(d,indent=2));src.rename(E/(old+tail.replace('.json','.archived.json')))
 if (P/'glb'/(old+'.glb')).exists():(P/'glb'/(old+'.glb')).rename(P/'glb'/(new+'.glb'))
# Clear description and alias decisions were made from the actual renders.
aliases={
 'wolf':['wolf','wolves','grey wolf','gray wolf'], 'wolf-realistic':['realistic wolf','real wolf','grey wolf','gray wolf','wolves'],
 'fox':['fox','foxes'], 'dog':['dog','dogs','husky','huskies'], 'deer':['deer','doe','does'], 'stag':['stag','stags','buck','antlered deer'],
 'horse':['horse','horses'], 'bull':['bull','bulls','cattle'], 'cow':['cow','cows','cattle'], 'alpaca':['alpaca','alpacas','llama'], 'donkey':['donkey','donkeys'],
 'orc':['orc','orcs'], 'yeti':['yeti','yetis','abominable snowman'], 'rabbit-brute':['rabbit monster','rabbit','bunny','bunnies'],
 'bird-brute':['bird monster','flightless bird'], 'dinosaur':['dinosaur','dinosaurs','dino'], 'frog-brute':['frog','frogs','poison frog'],
 'fish-raider':['fish monster','fish raider','fishman'], 'mushroom-king':['mushroom monster','mushroom king','fungus'],
 'cactus-monster':['cactus monster','cactus','cacti'], 'blue-demon':['demon','demons','blue demon'], 'alien':['alien','aliens'],
 'monkroose':['monkroose','beaked chimera','chimera brute'], 'tribal-monster':['masked monster','forest guardian'],
 'dragon':['dragon','dragons','flying dragon'], 'giant-bee':['bee','bees','giant insect'], 'flying-goblin':['flying goblin','goblin head'],
 'flying-bird':['flying bird','purple bird'], 'winged-imp':['imp','imps','winged imp'],
}
for p in sorted(E.glob('*-entry.json')):
 d=json.loads(p.read_text());kind=d['id'].removeprefix('enemy-');d['names']=aliases[kind]
 d['tint']=[x for x in d['tint'] if x.startswith('Main')]
 if kind=='wolf-realistic':
  d['about']='A textured grey wolf with natural proportions and eyes in its head; walks, gallops, lunges head-first and falls onto its side.'
  d['licence']='CC0-1.0';d['credit']='Forge, generated reference and Pixal3D mesh created for this library (CC0 dedication); Quaternius Ultimate Animated Animals rig and authored clips (CC0), https://quaternius.com/packs/ultimateanimatedanimals.html ; mesh source: https://infer.ohmyunicorn.com/pxmesh/forge-pixal3d-1ab0ac75ee6346e6afcc63df62eca9f2.glb'
 if kind in ['wolf','fox','dog','deer','stag','horse','bull','cow','alpaca','donkey','wolf-realistic']:
  d['good_for']=d['names']+['forest','wilderness','survival','rural'];d['mood']=['natural','wilderness']
 elif kind in ['cactus-monster']:d['good_for']=d['names']+['desert','fantasy','platformer']
 elif kind=='yeti':d['good_for']=d['names']+['snow','mountains','fantasy']
 else:d['good_for']=d['names']+['fantasy','platformer']
 p.write_text(json.dumps(d,indent=2))
print('PROMOTED',len(list((P/'candidates').glob('*.glb'))),'contact-corrected GLBs')
