import os,sys,json,hashlib,subprocess
from pathlib import Path

def load_env(path):
 for line in Path(path).read_text(errors='ignore').splitlines():
  line=line.strip()
  if line and not line.startswith('#') and '=' in line:
   k,v=line.split('=',1);os.environ.setdefault(k.strip(),v.strip().strip('"\''))
load_env('/opt/OpenMontage/.env');sys.path.insert(0,'/opt/OpenMontage')
from tools.graphics.cx_image import CXImage
P='gacmai_20260718_091744';R=Path('/data/video-pipeline/GacMaiAudio/project')/P;A=R/'artifacts';out=R/'assets/images';out.mkdir(parents=True,exist_ok=True)
contract=A/'visual_campaign_contract_rev1.json';csha=hashlib.sha256(contract.read_bytes()).hexdigest();assert csha=='3d345b87df3fa3d0c67458a6930b2cbf41a82f80ac7de3c4e7b9e5e920a16f9b'
base='Premium cinematic semi-realistic HAND-PAINTED GOUACHE-AND-OIL theatrical poster campaign, unmistakable expressive brush strokes on faces, clothes and props, simplified graphic tonal planes, hand-painted edges, subtle paper tooth, tactile but intentionally non-photographic materials, Vietnamese faces and hands, muted dark teal table-tennis hall with old tungsten amber accents, cream paper details, premium print grain. CLEARLY ILLUSTRATED POSTER ART. NOT photography, NOT film still, NOT photoreal, NOT CGI concept render, NOT plastic 3D, NOT flat vector, NOT anime. No readable text, no numbers, no logo, no watermark, no brand, no courtroom symbols, no accusation, no villain, no luxury sports arena. '
prompts={
'intro_source_rev3.png':('1920x1080',base+'NATIVE LANDSCAPE 16:9 hand-painted gouache/oil key art. EXACTLY one identifiable person: Nguyễn Hoài Lan, Vietnamese woman 42, low-tied black hair, blue-gray rolled-sleeve shirt, charcoal trousers, worn ivory canvas shoes. She waits behind a painted floor line holding a completely BLANK cream ticket and faded olive-brown canvas racket bag with old wooden handle. Empty scratched dark-teal table and cloth net lead to an enamel board containing ONLY solid abstract color rectangles and an analog wall clock containing ONLY twelve plain tick marks and two hands. ABSOLUTELY ZERO letters, numerals, pseudo-writing, glyphs, labels, brands, scribbles or marks on table apron, board, clock, ticket, walls, shoes or bag; table apron must be plain solid dark teal. One face only, no other person. Restrained tension, no accusation or triumph. Visible expressive brush strokes on face, clothes, props and table, simplified graphic tonal planes, premium theatrical illustrated poster, clearly not photograph or film still. Safe inset for all required objects.'),
'right_rail_source_rev3.png':('1080x1920',base+'NATIVE TRUE PORTRAIT 9:16 FULL-BLEED layered ensemble poster, exactly four identifiable Vietnamese adults and no fifth face. Hạnh woman 35 upper-left at abstract magnetic board, one hand paused. Dũng man 51 upper-right, spatially separate, LOOKING at a wall clock that has exactly two clear analog hands and tick marks; his own hands stay naturally at his sides and he never touches the board. Quang man 48 lower-left, serious not villain, holding blank cream paid-slot card with embossed punch and muted purple stamp shape. IMPORTANT: Quang entire silhouette from hair through BOTH COMPLETE SHOES must fit inside frame with at least 10 percent bottom margin and 10 percent left margin; no body part touches edge. Thư woman 29 lower-right ready with old table-tennis paddle; entire paddle head, handle and gripping hand visible with 10 percent bottom/right margins. Board has abstract shapes only. No readable text, numerals, pseudo-writing, logos. Restrained operational tension, no role/prop swap. Hand-painted gouache/oil cinematic semi-realism with visible brushwork and graphic tonal planes, not photo/CGI/flat/grid. Keep all four faces, hands, complete shoes and props safely inset.'),
}
provider=CXImage();results=[]
for name,(size,prompt) in prompts.items():
 path=out/name
 r=provider.execute({'prompt':prompt,'model':'cx/gpt-5.5-image','size':size,'quality':'high','background':'auto','image_detail':'high','output_format':'png','n':1,'output_path':str(path)})
 if not r.success:raise SystemExit(f'{name}: {r.error}')
 probe=subprocess.check_output(['ffprobe','-v','error','-select_streams','v:0','-show_entries','stream=width,height','-of','json',str(path)],text=True)
 stream=json.loads(probe)['streams'][0];w=int(stream['width']);h=int(stream['height'])
 results.append({'file':str(path),'sha256':hashlib.sha256(path.read_bytes()).hexdigest(),'requested_size':size,'measured_width':w,'measured_height':h,'measured_aspect':w/h,'provider':r.data.get('provider'),'model':r.model})
(A/'visual_generation_manifest_rev0.json').write_text(json.dumps({'status':'generated_pending_qa','contract_sha256':csha,'results':results},ensure_ascii=False,indent=2)+'\n')
print(json.dumps({'status':'generated_pending_qa','results':results},ensure_ascii=False))
