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 illustrated theatrical poster campaign, controlled painterly detail, tactile real materials, natural Vietnamese faces and hands, muted dark teal table-tennis hall with old tungsten amber accents, cream paper details, subtle premium print grain. NOT photography, NOT photoreal, 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_rev1.png':('1920x1080',base+'UNMISTAKABLY HAND-PAINTED SEMI-REALISTIC ILLUSTRATED THEATRICAL POSTER: visible elegant brush strokes, shaped-edge painting, stylized tonal planes, expressive painterly surfaces, clearly not a photograph, not a film still, not photoreal CGI. NATIVE LANDSCAPE 16:9 KEY ART, authored as a wide composition. Exactly one identifiable person: Nguyễn Hoài Lan, Vietnamese woman age 42, slim-average build, angular oval face, natural light-brown skin, deep brown tired eyes, black shoulder-length hair loosely tied low. Blue-gray cotton shirt sleeves rolled to forearms, straight charcoal trousers, worn ivory canvas shoes. She stands just behind a painted waiting line at left third, holding a small curled cream ticket and faded olive-brown canvas racket bag with old wooden racket handle visible. Restrained tense posture, looking at an empty scratched dark-teal table number four implied spatially, cloth net. Central low perspective leads to an enamel magnetic status board with abstract unreadable color tiles and a scratched wall clock with no legible marks in middle-right depth. Cool fluorescent teal over table, old amber rim on Lan face and hands. Lan does not cross line, touch board, accuse or celebrate. No other people or identifiable faces. Uncluttered upper-left negative space. Chest-height 40mm natural perspective, premium cinematic illustrated poster, not event photo.'),
'left_rail_source_rev1.png':('1080x1920',base+'NATIVE TRUE PORTRAIT 9:16 FULL-BLEED LEFT RAIL, composed vertically from scratch, never cropped from landscape. Exactly one identifiable person, same Nguyễn Hoài Lan continuity: age 42, same angular oval face, low tied black hair, blue-gray rolled-sleeve shirt, charcoal trousers, worn ivory canvas shoes. Full body head-to-shoes visible, occupying STRICTLY 73-75 percent of frame height with generous floor and headroom, standing beside waiting wall just behind a diagonal painted line. One hand holds curled cream ticket, faded olive-brown canvas racket bag hangs vertically and opens enough to show worn wooden racket handle. Dignified restrained waiting, vulnerable but not helpless, no heroic pose. Teal table blur in deep background, warm doorway rim on shoulder and ticket, cool reflected fill below, long soft shadow on terrazzo. Native portrait negative space upper-left, all limbs/ticket/bag safe from edges. No other faces, no text, no horizontal-card composition, no letterbox.'),
'right_rail_source_rev1.png':('1080x1920',base+'NATIVE TRUE PORTRAIT 9:16 FULL-BLEED RIGHT RAIL ENSEMBLE, composed vertically from scratch, not a crop, not headshot grid. Exactly four identifiable Vietnamese adults, layered vertically with separate roles and natural anatomy. Top-left near enamel magnetic board: Lê Thu Hạnh woman 35, compact, round face, black low bun, cream shirt and open gray-olive jacket, one hand paused near abstract unreadable tile, not commanding. Upper-right separated under a scratched wall clock WITH TWO CLEAR ANALOG HANDS showing a generic non-readable time: Phạm Quốc Dũng man 51, sturdy, soft square face, short black-gray hair, navy logo-free polo and gray khaki, visibly checking the clock by gaze only, hands away from board. Lower-left behind waiting line: Trần Minh Quang man 48, firm average build, long face, gray temples, muted brick athletic shirt, holding a small cream paid-slot card close to chest; the card has one obvious embossed circular punch and a muted purple validation stamp shape, but NO readable letters or numbers, visually signaling paid/reserved status; serious not villainous. Lower-right foreground ready for court: Vũ Minh Thư woman 29, agile, heart-shaped face, warm brown skin, messy high ponytail, pale mustard tee under blue-gray overshirt, black sport trousers, old unbranded racket lowered at thigh. Lines of sight converge into empty space between board and waiting line. No Lan, no Phúc, no fifth face, no anonymous faces. Restrained operational tension, four roles never exchange props or authority. Teal/charcoal campaign palette, amber accents, all faces and meaningful hands/props clear and uncut, premium cinematic ensemble poster, no text.'),
}
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))
