from pathlib import Path
import requests,json,base64,time,sys,hashlib
from datetime import datetime
from zoneinfo import ZoneInfo
P=Path('/opt/creature-lib-20261017');out=P/'realistic';out.mkdir(exist_ok=True)
animals={
'stag':'adult male red deer stag with a symmetrical naturally branched pair of modest six-point antlers, rich reddish brown summer coat, dark thick neck, pale rump, short tail, slender cloven hooves. The antlers are clearly separated from the ears, no exaggerated giant rack',
'deer':'adult female red deer hind, NO antlers, NO horns, warm tan brown summer coat, pale cream underside and rump, large upright ears, small dark eyes, slender long legs with dark cloven hooves, short tail',
'fox':'adult European red fox, rusty orange fur, white chin and chest, black lower legs, black ear backs, slender pointed muzzle, small dark amber eyes, very bushy long tail pointing backward and slightly downward with a white tip, clearly separated from hind legs',
'dog':'large athletic dark charcoal black and brown hound dog, natural short sleek dark fur with subtle tan muzzle and paws, muscular deep chest, long legs, long muzzle, floppy hanging ears, long tail hanging behind and apart from hind legs. A real working hunting hound, NOT a husky, NOT a wolf, NO pointed upright ears, NO white face mask, NO collar',
'horse':'adult bay horse, realistic athletic riding horse proportions, reddish brown short coat, black mane lying down one side of neck, dark lower legs, black tail hanging behind and separated from hind legs, long face, small eyes, erect short ears, four dark hooves, no saddle, no bridle, no tack'
}
for kind in (sys.argv[1:] or list(animals)):
 if datetime.now(ZoneInfo('Europe/Paris')).strftime('%H:%M')>='21:27':print('STOP new generation cutoff CET',flush=True);break
 d=out/kind;d.mkdir(exist_ok=True)
 prompt='Photorealistic wildlife studio photograph of one '+animals[kind]+'. Neutral relaxed standing quadruped pose. Full body SIDE VIEW, head facing LEFT, with a very slight front angle so ALL FOUR legs and feet are visible and separated. Straight natural back, head held naturally and looking level, mouth closed. All four feet planted on the same level ground. Accurate real animal anatomy, realistic proportions, eyes naturally seated in sockets. Plain pure white seamless background, no cast shadow. Entire animal fully inside the frame with generous margins including ears, hooves and tail. Fine photographic surface detail. A REAL ANIMAL photographed, not a cartoon, not Disney, not a toy, not a statue, not a 3D render. No text, no people, no props.'
 if not (d/'reference.png').exists():
  req={'prompt':prompt,'size':'1024x1024','n':1,'response_format':'b64_json'};(d/'reference-request.json').write_text(json.dumps(req,indent=2))
  r=requests.post('http://127.0.0.1:8093/v1/images/generations',json=req,timeout=210);r.raise_for_status();data=r.json();im=data['data'][0]
  if 'b64_json' in im:raw=base64.b64decode(im['b64_json'])
  else:raw=requests.get(im['url'].replace('http://forge-vertex:8080','http://127.0.0.1:8093'),timeout=60).content
  (d/'reference.png').write_bytes(raw)
  (d/'provenance.json').write_text(json.dumps({'author':'Forge generated reference and Pixal3D mesh','input':'No third-party input picture','model':data.get('model','service default'),'prompt':prompt,'reference_sha256':hashlib.sha256(raw).hexdigest(),'started_CET':datetime.now(ZoneInfo('Europe/Paris')).isoformat()},indent=2))
  print(kind,'REFERENCE',len(raw),flush=True)
 if (d/'raw.glb').exists():continue
 if (d/'submission.json').exists():sub=json.loads((d/'submission.json').read_text())
 else:
  # Exactly one outstanding GPU job from this worker; do not occupy a busy queue.
  while requests.get('http://127.0.0.1:8087/v1/3d/queue',timeout=15).json()['depth']:time.sleep(10)
  with (d/'reference.png').open('rb') as f:r=requests.post('http://127.0.0.1:8087/v1/3d/jobs',files={'image':('reference.png',f,'image/png')},data={'seed':47017,'max_faces':14000,'bake':'false'},timeout=30)
  r.raise_for_status();sub=r.json();(d/'submission.json').write_text(json.dumps(sub,indent=2))
 job=sub.get('job_id',sub.get('id'));print(kind,'JOB',job,flush=True)
 for _ in range(180):
  result=requests.get('http://127.0.0.1:8087/v1/3d/jobs/'+job,timeout=20).json()
  if result['status'] in ['done','failed']:break
  time.sleep(8)
 else:raise TimeoutError(kind)
 (d/'job-final.json').write_text(json.dumps(result,indent=2));print(kind,result['status'],result.get('error'),flush=True)
 if result['status']!='done':continue
 data=result['result'];item=data['data'][0] if 'data' in data else data
 for k,n in [('url','raw.glb'),('preview_url','preview.png')]:
  if item.get(k):
   r=requests.get('http://127.0.0.1:8087/cache/'+item[k].split('/')[-1],timeout=90);r.raise_for_status();(d/n).write_bytes(r.content)
 print(kind,'COLLECTED',flush=True)
