#!/usr/bin/env python3
from pathlib import Path
import json, re
PROJECT=Path('/data/video-pipeline/Youtube-Video-Maker/Project/03072026-004-Phan-Dinh-Giot')
SRC=PROJECT/'script/source_story.txt'
def clean(t): return re.sub(r'\n{3,}','\n\n',t.replace('\r\n','\n').replace('\r','\n').strip())
def sents(p): return [s.strip() for s in re.split(r'(?<=[.!?])\s+',p.strip()) if s.strip()]
def title(x):
 t=re.sub(r'^\d+\.\s*','',x.strip()); return t[:1]+t[1:].lower()
def visual(text, sec, typ='visual_beat'):
 low=text.lower()
 if typ=='heading': return f'Title card for section: {sec}'
 if 'lỗ châu mai' in low or 'hỏa điểm' in low: return 'Phan Đình Giót lao lên lấp lỗ châu mai tại cứ điểm Him Lam, khói lửa và đồng đội xung phong phía sau.'
 if 'him lam' in low or '13 tháng 3' in low: return 'Đêm Him Lam mở màn Điện Biên Phủ, pháo nổ, chiến hào, hàng rào dây thép gai và hỏa lực dữ dội.'
 if 'điện biên phủ' in low or 'de castries' in low: return 'Thung lũng Điện Biên Phủ, tập đoàn cứ điểm, chiến hào siết vòng vây và cờ chiến thắng trong sương núi.'
 if 'hà tĩnh' in low or 'cẩm xuyên' in low or 'cẩm quan' in low: return 'Làng quê nghèo Hà Tĩnh miền Trung, người nông dân trẻ lớn lên trong lam lũ và nắng gió.'
 if 'đại đoàn 312' in low or 'quân đội' in low or 'người lính' in low: return 'Người lính Cụ Hồ hành quân, rèn mình trong kháng chiến chống Pháp, ba lô, súng trường và đường rừng.'
 if 'kéo pháo' in low or 'dân công' in low: return 'Bộ đội và dân công kéo pháo qua núi rừng Tây Bắc, mồ hôi và ý chí trong đêm.'
 return 'Cảnh lịch sử Việt Nam kháng chiến chống Pháp, người lính và chiến trường Điện Biên bằng mực nâu trên giấy cổ.'
def build(cleaned):
 body=re.sub(r'^Tiêu đề truyện:.*?^Nội dung:\s*','',cleaned,flags=re.S|re.M).strip(); chunks=re.split(r'(?m)^(\d+\.\s+[^\n]+)$',body)
 scenes=[]; sid=1; sec='Mở đầu'
 for ch in chunks:
  if not ch.strip(): continue
  if re.match(r'^\d+\.\s+',ch.strip()):
   sec=title(ch); scenes.append({'scene_id':f'scene_{sid:03d}','type':'heading','section':sec,'text':sec,'visual_beat':visual('',sec,'heading'),'estimated_seconds':4,'rule':'heading_must_be_own_scene'}); sid+=1; continue
  for p in [x.strip() for x in ch.split('\n\n') if x.strip()]:
   group=[]; wc=0
   for s in sents(p):
    group.append(s); wc+=len(s.split())
    if wc>=42 or len(group)>=3:
     txt=' '.join(group); scenes.append({'scene_id':f'scene_{sid:03d}','type':'visual_beat','section':sec,'text':txt,'visual_beat':visual(txt,sec),'estimated_seconds':max(14,min(22,round(len(txt.split())/3.0))),'rule':'grouped_visual_beat_14_22_seconds'}); sid+=1; group=[]; wc=0
   if group:
    txt=' '.join(group)
    if scenes and scenes[-1]['type']=='visual_beat' and scenes[-1]['section']==sec and len(txt.split())<18:
     scenes[-1]['text']+=' '+txt; scenes[-1]['visual_beat']=visual(scenes[-1]['text'],sec); scenes[-1]['estimated_seconds']=min(22,scenes[-1]['estimated_seconds']+5)
    else:
     scenes.append({'scene_id':f'scene_{sid:03d}','type':'visual_beat','section':sec,'text':txt,'visual_beat':visual(txt,sec),'estimated_seconds':max(12,min(22,round(len(txt.split())/3.0))),'rule':'grouped_visual_beat_14_22_seconds'}); sid+=1
 return scenes
chars=[
 {'name':'Phan Đình Giót người lính','role':'Main hero, soldier of Đại đoàn 312 before Him Lam','estimated_age':'31-32','appearance':'Vietnamese man from Hà Tĩnh, lean strong farmer-soldier build, sun-darkened face, determined eyes, modest expression','costume':'Viet Minh soldier uniform, simple green/brown clothes, pith helmet or cloth cap, rifle and pack','temperament':'quiet, tough, brave, selfless','historical_cultural_context':'First Indochina War, Điện Biên Phủ campaign 1954','visual_keywords':['Phan Dinh Giot','soldier','Dien Bien Phu','Viet Minh'],'negative_notes':'no modern uniform, no American gear, no fantasy'},
 {'name':'Phan Đình Giót tại lỗ châu mai','role':'Main hero in final assault at Him Lam','estimated_age':'32','appearance':'wounded Vietnamese soldier, intense face, dirt and smoke, heroic but historically restrained','costume':'battle-worn Viet Minh uniform, simple equipment, no gore emphasis','temperament':'decisive, sacrificial, fearless','historical_cultural_context':'Him Lam strongpoint, opening night of Điện Biên Phủ, 13 March 1954','visual_keywords':['embrasure','Him Lam','final sacrifice'],'negative_notes':'no gore spectacle, no superhero pose, no modern gear'},
 {'name':'Bộ đội Đại đoàn 312','role':'Comrades and assault troops at Him Lam','estimated_age':'young adult to middle-aged','appearance':'Vietnamese soldiers, muddy, exhausted, determined, carrying rifles, bộc phá, ladders, wire cutters','costume':'Viet Minh 1954 field uniforms, cloth caps, sandals, packs','temperament':'disciplined, brave, urgent','historical_cultural_context':'312th Division attack on Him Lam','visual_keywords':['Viet Minh assault troops','Him Lam','trenches'],'negative_notes':'no modern helmets/body armor'},
 {'name':'Quân Pháp trong cứ điểm Him Lam','role':'Adversary garrison, bunkers, machine-gun positions','estimated_age':'adult soldiers','appearance':'French Union soldiers in bunkers, tense, silhouettes behind fortifications','costume':'1950s French colonial uniforms, helmets, sandbags, bunkers','temperament':'defensive, entrenched, alarmed','historical_cultural_context':'French strongpoint at Điện Biên Phủ','visual_keywords':['French bunker','machine gun','barbed wire'],'negative_notes':'no WWII Nazi styling, no modern NATO gear'},
 {'name':'Dân công và pháo binh Điện Biên','role':'Support forces pulling artillery and supplying campaign','estimated_age':'varied adults','appearance':'Vietnamese dân công, artillery crews, strained faces, ropes on shoulders, mountain terrain','costume':'simple rural clothes and Viet Minh support uniforms, nón lá, backpacks','temperament':'enduring, collective, determined','historical_cultural_context':'logistics and artillery preparation for Điện Biên Phủ','visual_keywords':['pulling artillery','dan cong','Tay Bac mountains'],'negative_notes':'no modern trucks as main focus'}]
def main():
 cleaned=clean(SRC.read_text(encoding='utf-8')); (PROJECT/'script/cleaned_story.txt').write_text(cleaned,encoding='utf-8')
 scenes=build(cleaned)
 (PROJECT/'script/scenes.json').write_text(json.dumps({'project_id':PROJECT.name,'target_beat_seconds':'14-22','split_rule':'Headings standalone; grouped 14-22s visual beats.','total_scenes':len(scenes),'scenes':scenes},ensure_ascii=False,indent=2),encoding='utf-8')
 (PROJECT/'script/scenes_validation.json').write_text(json.dumps({'project_id':PROJECT.name,'total_scenes':len(scenes),'metadata_removed_from_scenes':True,'headings_standalone':True,'each_scene_has_visual_beat':True,'status':'passed'},ensure_ascii=False,indent=2),encoding='utf-8')
 bible={'project_id':PROJECT.name,'visual_style_global':'16:9 horizontal historical storytelling image, Điện Biên Phủ 1954, ink-wash window centered on warm antique paper, muted sepia olive charcoal red-earth watercolor, no modern/fantasy drift.','consistency_rules':['Use Phan Đình Giót người lính for life/training/campaign scenes.','Use Phan Đình Giót tại lỗ châu mai for the final Him Lam action.','Keep Viet Minh and French 1954 uniforms/fortifications accurate.'],'characters':chars}
 (PROJECT/'script/character_bible.json').write_text(json.dumps(bible,ensure_ascii=False,indent=2),encoding='utf-8')
 dense=[]
 for sc in scenes:
  prompt=('16:9 horizontal historical storytelling image, ink-wash narrative window on warm antique paper, painted content only 65-70% of frame with clean parchment negative space. '+f"Scene: {sc['visual_beat']} Narration beat: {sc['text']} Historical grounding: Hà Tĩnh rural life, First Indochina War, Điện Biên Phủ 1954, Him Lam strongpoint, accurate Viet Minh uniforms, French bunkers, barbed wire, trenches, artillery and mountain terrain. Negative: no modern objects, no American Vietnam War gear, no fantasy armor, no gore spectacle, no 3D game art, no full-frame glossy poster, no text labels or watermark.")
  dense.append({**sc,'summary':sc['visual_beat'],'narration':sc['text'],'visual_prompt':prompt})
 (PROJECT/'script/dense_scene_plan.json').write_text(json.dumps({'project_id':PROJECT.name,'approved_for_next_step':True,'total_scenes':len(dense),'scenes':dense},ensure_ascii=False,indent=2),encoding='utf-8')
 print(json.dumps({'project':str(PROJECT),'total_scenes':len(scenes)},ensure_ascii=False))
if __name__=='__main__': main()
