#!/usr/bin/env python3
import concurrent.futures, json, os, subprocess, sys, time, re, unicodedata
from pathlib import Path
PROJECT=Path('/data/video-pipeline/Youtube-Video-Maker/Project/02072026-001-Nha-Nguyen')
GEN_SCRIPT=Path('/home/hermes/.hermes/skills/media/gen-img-gpt/scripts/gen_img_gpt.py')
SCENES=PROJECT/'script/scenes.json'; PLAN=PROJECT/'script/character_reference_plan.json'
OUT_SCENES=PROJECT/'images/scenes'; OUT_THUMBS=PROJECT/'output/thumbnails'; LOG=PROJECT/'logs/step06_images_run.log'; MANIFEST=PROJECT/'logs/step06_images_manifest.json'
WORKERS=int(os.environ.get('STEP06_WORKERS','5'))
STYLE_SCENE='16:9 horizontal historical storytelling image, centered irregular ink-wash narrative window on warm antique cream paper, painted content occupies only about 65-70 percent of page with 30-35 percent clean antique paper negative space, handcrafted Vietnamese co phong feeling, muted brown black charcoal watercolor wash, not modern, not full-frame glossy digital poster, not fantasy splash art, not 3D game key art, no text, no watermark.'
STYLE_HEADING='16:9 horizontal light Vietnamese old-calligraphy title-card on warm antique paper, subtle ink wash and small red seal stamp accent, airy text with breathing room, not heavy black title slam, not clean modern typography.'
NEG='Avoid modern objects, wrong culture, fantasy armor, samurai, character sheet layout, model sheet, multiple views, floating costume parts, text labels, watermark, logo, glossy poster, edge-to-edge full-frame painting.'
def slug(s):
    s=unicodedata.normalize('NFD',s); s=''.join(c for c in s if unicodedata.category(c)!='Mn'); s=s.replace('đ','d').replace('Đ','D')
    return re.sub(r'[^a-zA-Z0-9]+','-',s).strip('-').lower()
def load_refs():
    refs={}
    if PLAN.exists():
        for r in json.loads(PLAN.read_text(encoding='utf-8')).get('references',[]): refs[r['character_id']]=Path(r['output_path'])
    return refs
def log(msg):
    line=time.strftime('%Y-%m-%d %H:%M:%S ')+msg; print(line,flush=True); LOG.parent.mkdir(parents=True,exist_ok=True)
    with LOG.open('a',encoding='utf-8') as f: f.write(line+'\n')
def scene_refs(text, refs):
    t=text.lower(); keys=[]
    mapping=[('nguyễn ánh','gia-long-nguyen-anh'),('gia long','gia-long-nguyen-anh'),('minh mạng','minh-mang'),('tự đức','tu-duc'),('bảo đại','bao-dai'),('hàm nghi','ham-nghi'),('thành thái','thanh-thai'),('duy tân','duy-tan'),('khải định','khai-dinh'),('tôn thất thuyết','ton-that-thuyet'),('nguyễn tri phương','nguyen-tri-phuong'),('phan thanh giản','phan-thanh-gian'),('trương định','truong-dinh'),('nguyễn trung trực','nguyen-trung-truc')]
    for token,k in mapping:
        if token in t: keys.append(k)
    if any(x in t for x in ['pháp','thực dân','đà nẵng','gia định','thuận an','hòa ước','bảo hộ','đại bác','tàu chiến','garnier','rivière']): keys.append('quan-phap-xam-luoc')
    if any(x in t for x in ['dân chúng','sĩ phu','nghĩa sĩ','cần vương','làng','người dân','phong trào','kháng pháp','nông dân']): keys.append('dan-chung-va-si-phu-dai-nam')
    out=[]
    for k in keys:
        p=refs.get(k)
        if p and p.exists() and p not in out: out.append(p)
    return out
def prompt_for(scene, refs):
    if scene.get('type')=='heading':
        return f"{STYLE_HEADING}\nVietnamese title card text to depict accurately if possible: {scene['narration']}\nSubtle Nguyen dynasty motifs, Hue citadel silhouettes, imperial seals, river mist, old maps and broken treaty papers, antique paper. {NEG}"
    sr=scene_refs(scene.get('narration','')+' '+scene.get('summary',''), refs)
    ref_note='Use attached character reference image(s) only to keep identity, age, face, costume language, and temperament consistent. Do not reproduce a reference sheet. ' if sr else ''
    return f"{STYLE_SCENE}\nHistorical context: Vietnam under the Nguyen dynasty from 1802 to 1945, Hue imperial court, Gia Long reunification, Minh Mang centralization, Tu Duc crisis, French invasion, Can Vuong, colonial protectorate, Bao Dai abdication.\nNarration beat: {scene['narration']}\nVisual direction: {scene.get('visual_beat') or scene.get('visual_prompt')}\n{ref_note}One cinematic narrative scene, historically grounded Vietnamese Nguyen dynasty clothing, Hue citadel architecture, villages, rivers, court halls, treaty rooms, war camps, French gunboats only when relevant.\nNegative exclusions: {NEG}"
def run_one(scene, refs):
    out=OUT_SCENES/f"{scene['scene_id']}.png"
    if out.exists() and out.stat().st_size>1000: return {'scene_id':scene['scene_id'],'status':'skipped_exists','output':str(out),'bytes':out.stat().st_size}
    cmd=[sys.executable,str(GEN_SCRIPT),'--prompt',prompt_for(scene, refs),'--output',str(out),'--size','auto','--quality','auto','--background','auto','--image-detail','high','--output-format','png','--n','1']
    for ref in scene_refs(scene.get('narration','')+' '+scene.get('summary',''), refs)[:2]: cmd += ['--input-ref',str(ref)]
    p=subprocess.run(cmd,text=True,capture_output=True,timeout=1200)
    if p.returncode!=0: return {'scene_id':scene['scene_id'],'status':'failed','output':str(out),'error':(p.stderr or p.stdout)[-2000:]}
    return {'scene_id':scene['scene_id'],'status':'ok','output':str(out),'bytes':out.stat().st_size if out.exists() else 0}
def thumb_prompt(kind):
    aspect='16:9 YouTube thumbnail, 1920x1080' if kind=='16x9' else 'TikTok-safe vertical thumbnail/poster, 1080x1280 preferred composition'
    return f"{aspect}. Cinematic Vietnamese historical dynasty poster for 'NHÀ NGUYỄN - TRIỀU ĐẠI CUỐI CÙNG'. Large dramatic Nguyen emperor silhouette before Hue citadel and Bao Dai abdication seal-sword motif, shadow of French gunboat and broken treaty papers, dark sepia bronze black palette, parchment texture, large readable metallic-gold Vietnamese brush lettering over irregular black ink-splash title panel. Historically grounded, not modern, not fantasy, no watermark."
def gen_thumb(kind,out,refs):
    if out.exists() and out.stat().st_size>1000: return {'kind':kind,'status':'skipped_exists','output':str(out),'bytes':out.stat().st_size}
    cmd=[sys.executable,str(GEN_SCRIPT),'--prompt',thumb_prompt(kind),'--output',str(out),'--size','auto','--quality','auto','--background','auto','--image-detail','high','--output-format','png','--n','1']
    p=refs.get('bao-dai') or refs.get('gia-long-nguyen-anh')
    if p and p.exists(): cmd += ['--input-ref',str(p)]
    r=subprocess.run(cmd,text=True,capture_output=True,timeout=1200)
    if r.returncode!=0: return {'kind':kind,'status':'failed','output':str(out),'error':(r.stderr or r.stdout)[-2000:]}
    return {'kind':kind,'status':'ok','output':str(out),'bytes':out.stat().st_size if out.exists() else 0}
def main():
    OUT_SCENES.mkdir(parents=True,exist_ok=True); OUT_THUMBS.mkdir(parents=True,exist_ok=True); LOG.parent.mkdir(parents=True,exist_ok=True); LOG.write_text('',encoding='utf-8')
    refs=load_refs(); scenes=json.loads(SCENES.read_text(encoding='utf-8'))['scenes']; results=[]; log(f'START step6 scenes={len(scenes)} workers={WORKERS}')
    with concurrent.futures.ThreadPoolExecutor(max_workers=WORKERS) as ex:
        futs={ex.submit(run_one,s,refs):s for s in scenes}
        for fut in concurrent.futures.as_completed(futs):
            r=fut.result(); results.append(r); log((f"{r['status']} {r.get('scene_id')} bytes={r.get('bytes')}" if 'scene_id' in r else str(r)))
    thumb_results=[]
    if not any(r['status']=='failed' for r in results):
        log('START thumbnails')
        for kind,name in [('16x9','thumb_16x9_final.png'),('9x16','thumb_9x16_final.png')]:
            tr=gen_thumb(kind,OUT_THUMBS/name,refs); thumb_results.append(tr); log(str(tr))
    status='completed' if all(r['status'] in ('ok','skipped_exists') for r in results) and len(thumb_results)==2 and all(r['status'] in ('ok','skipped_exists') for r in thumb_results) else 'needs_retry'
    MANIFEST.write_text(json.dumps({'project_id':PROJECT.name,'step':6,'status':status,'workers':WORKERS,'total_scenes':len(scenes),'scene_results':results,'thumbnail_results':thumb_results},ensure_ascii=False,indent=2),encoding='utf-8')
    log('DONE '+json.dumps({'status':status,'scenes_done':sum(1 for r in results if r['status'] in ('ok','skipped_exists')),'scenes_total':len(scenes),'thumbs':len(thumb_results)},ensure_ascii=False))
    if status!='completed': raise SystemExit('needs_retry')
if __name__=='__main__': main()
