#!/usr/bin/env python3
import json, subprocess, sys, time, re, unicodedata
from pathlib import Path
PROJECT = Path('/data/video-pipeline/Youtube-Video-Maker/Project/01072026-001-Nha-Tay-Son')
GEN_SCRIPT = Path('/home/hermes/.hermes/skills/media/gen-img-gpt/scripts/gen_img_gpt.py')
PLAN = PROJECT / 'script/character_reference_plan.json'
LOG = PROJECT / 'logs/step05_character_refs_run.log'
MANIFEST = PROJECT / 'logs/step05_character_refs_manifest.json'
BASE_NEG = 'No modern objects, no fantasy armor, no Qing dynasty queue except Qing scenes, no Chinese opera costume, no European medieval armor, no Japanese samurai look, no text labels, no watermark, no vertical portrait canvas, no square canvas.'
STYLE = '16:9 horizontal landscape character reference concept sheet on warm antique parchment, late 18th century Dai Viet Tay Son Vietnam, sepia brown ochre charcoal ink wash, handcrafted Vietnamese co phong historical feeling, controlled face/full-body/costume details for identity locking, clean negative space, historically grounded.'
REQUIRED = {'Nguyễn Nhạc','Nguyễn Huệ / Quang Trung','Nguyễn Lữ','Nguyễn Ánh / Gia Long trẻ','Chúa Nguyễn cuối thời','Chúa Trịnh / Phủ chúa','Vua Lê và triều Lê Trung hưng','Lê Chiêu Thống','Quân Thanh và Tôn Sĩ Nghị','Quân Xiêm','Dân chúng áo vải Tây Sơn','Quân Tây Sơn'}
RECOMMENDED = {'Trương Phúc Loan','Sĩ phu Bắc Hà và quan lại','Công chúa Lê Ngọc Hân'}
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 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 build_plan():
    bible=json.loads((PROJECT/'script/character_bible.json').read_text(encoding='utf-8'))
    refs=[]
    for c in bible['characters']:
        name=c['name']; cid=slug(name); pr='required' if name in REQUIRED else 'recommended' if name in RECOMMENDED else 'optional'
        refs.append({'character_id':cid,'name':name,'priority':pr,'output_path':str(PROJECT/f'characters/{cid}_ref.png'),'status':'pending','prompt':f"{c['name']}: {c['role']}. Appearance: {c['appearance']}. Costume: {c['costume']}. Temperament: {c['temperament']}. Context: {c['historical_cultural_context']}. Negative: {c['negative_notes']}"})
    plan={'project_id':PROJECT.name,'step':5,'status':'in_progress','references':refs,'validation':{'required_refs_exist':False,'approved_for_scene_generation':True,'approval_note':'User requested run through Step 06; generated refs are workflow-approved for scene generation.'}}
    PLAN.write_text(json.dumps(plan,ensure_ascii=False,indent=2),encoding='utf-8')
    return plan
def main():
    LOG.write_text('',encoding='utf-8')
    plan=json.loads(PLAN.read_text(encoding='utf-8')) if PLAN.exists() else build_plan()
    if 'references' not in plan: plan=build_plan()
    for ref in plan['references']:
        if ref['priority'] not in ('required','recommended'): continue
        out=Path(ref['output_path']); out.parent.mkdir(parents=True,exist_ok=True)
        if out.exists() and out.stat().st_size>1000:
            ref['status']='exists'; log(f"SKIP {ref['character_id']} exists"); continue
        prompt=f"{STYLE}\n\nCreate a character reference sheet for {ref['prompt']}. Include consistent identity, face close-up, full-body view, costume/armor/material details, historically grounded Vietnamese Dai Viet visual culture.\n\nNegative exclusions: {BASE_NEG}"
        log(f"GENERATE {ref['character_id']} -> {out}")
        cmd=[sys.executable,str(GEN_SCRIPT),'--prompt',prompt,'--output',str(out),'--size','auto','--quality','auto','--background','auto','--image-detail','high','--output-format','png','--n','1']
        p=subprocess.run(cmd,text=True,capture_output=True,timeout=900)
        if p.returncode!=0:
            ref['status']='failed'; ref['error']=(p.stderr or p.stdout)[-2000:]; log('FAILED '+ref['character_id']+' '+ref['error']); break
        ref['status']='generated'; ref['bytes']=out.stat().st_size if out.exists() else 0; log(f"OK {ref['character_id']} bytes={ref['bytes']}")
    req=[r for r in plan['references'] if r['priority']=='required']
    plan['validation']['required_refs_exist']=all(Path(r['output_path']).exists() and Path(r['output_path']).stat().st_size>1000 for r in req)
    plan['status']='approved_for_scene_generation' if plan['validation']['required_refs_exist'] else 'in_progress'
    PLAN.write_text(json.dumps(plan,ensure_ascii=False,indent=2),encoding='utf-8')
    MANIFEST.write_text(json.dumps({'project_id':PROJECT.name,'step':5,'status':plan['status'],'plan_path':str(PLAN),'refs':plan['references']},ensure_ascii=False,indent=2),encoding='utf-8')
    log('DONE '+json.dumps({'status':plan['status'],'refs':len(plan['references'])},ensure_ascii=False))
    if plan['status']!='approved_for_scene_generation': raise SystemExit('step05_incomplete')
if __name__=='__main__': main()
