#!/usr/bin/env python3
import json, re, datetime
from pathlib import Path

PROJECT = Path('/data/video-pipeline/Youtube-Video-Maker/Project/25012026-001-Thanh-Cat-Tu-Han')
NOW = lambda: datetime.datetime.now().isoformat(timespec='seconds')
SOURCE = PROJECT / 'script/source_story.txt'
CLEAN = PROJECT / 'script/cleaned_story.txt'
SCENES = PROJECT / 'script/scenes.json'
SCENE_VAL = PROJECT / 'script/scenes_validation.json'
BIBLE = PROJECT / 'script/character_bible.json'
STEP4 = PROJECT / 'script/dense_plan_validation.json'

SECTION_TITLES = {
    '1': 'Đứa trẻ sinh ra trên thảo nguyên gió lạnh',
    '2': 'Cái chết của Dã Tốc Cai và những ngày bị bỏ rơi',
    '3': 'Börte, anda và những liên minh đầu tiên',
    '4': 'Từ kẻ bị săn đuổi đến người tập hợp thảo nguyên',
    '5': 'Năm 1206 và danh hiệu Thành Cát Tư Hãn',
    '6': 'Cỗ máy chiến tranh Mông Cổ',
    '7': 'Quay về phía nam: cuộc chiến với Tây Hạ và nhà Kim',
    '8': 'Biến cố Otrar và cơn thịnh nộ hướng về phương Tây',
    '9': 'Người xây đế chế bằng luật, sợ hãi và cơ hội',
    '10': 'Những ngày cuối trên đường chinh phạt',
    '11': 'Bóng vó ngựa còn in trên lục địa',
}

def clean_text(txt):
    txt = txt.replace('\r\n', '\n').replace('\r', '\n')
    txt = re.sub(r'[ \t]+', ' ', txt)
    return re.sub(r'\n{3,}', '\n\n', txt).strip() + '\n'

def extract_meta(txt):
    title = ''; series = ''; body = []; in_body = False
    for line in txt.splitlines():
        s = line.strip(); low = s.lower()
        if low.startswith('tiêu đề truyện:'):
            title = s.split(':', 1)[1].strip()
        elif low.startswith('series:'):
            series = s.split(':', 1)[1].strip()
        elif low.startswith('nội dung:'):
            in_body = True
        elif in_body or (s and not low.startswith(('tiêu đề truyện:', 'series:'))):
            body.append(s)
    return title, series, '\n\n'.join(body).strip()

def split_sentences(text):
    return [p.strip() for p in re.split(r'(?<=[.!?])\s+', text.strip()) if p.strip()]

def chunk_para(para, max_chars=360):
    chunks = []; cur = ''
    for s in split_sentences(para):
        if cur and len(cur) + len(s) + 1 > max_chars:
            chunks.append(cur.strip()); cur = s
        else:
            cur = (cur + ' ' + s).strip()
    if cur: chunks.append(cur.strip())
    return chunks

def visual_for(text, section):
    t = text.lower()
    if 'temüjin' in t or 'temujin' in t or 'thành cát tư hãn' in t or 'chinggis' in t: return 'Temüjin / Thành Cát Tư Hãn trên thảo nguyên Mông Cổ, ánh mắt cứng rắn, vó ngựa và cờ chiến trong gió lạnh.'
    if 'hoelun' in t: return 'Hoelun dẫn các con qua thảo nguyên khắc nghiệt, người mẹ Mông Cổ mạnh mẽ giữa đói rét và tuyết gió.'
    if 'dã tốc cai' in t or 'yesügei' in t: return 'Dã Tốc Cai, thủ lĩnh Borjigin, bên lều trại và chiến mã trước khi số phận gia tộc đổi hướng.'
    if 'börte' in t or 'borte' in t: return 'Börte trong trang phục phụ nữ Mông Cổ quý tộc, bên lều nỉ, biểu tượng liên minh và danh dự.'
    if 'jamukha' in t: return 'Jamukha, anda rồi đối thủ của Temüjin, đứng giữa quý tộc thảo nguyên và bóng phản bội.'
    if 'toghrul' in t or 'vương hãn' in t or 'kereit' in t: return 'Toghrul Vương Hãn và liên minh Kereit trên thảo nguyên, cờ xí và lều trại du mục.'
    if 'jebe' in t or 'subutai' in t: return 'Các tướng Mông Cổ như Jebe và Subutai chỉ huy kỵ binh, bản đồ chiến dịch trải rộng Á Âu.'
    if 'tatar' in t or 'merkit' in t or 'tayichiud' in t or 'naiman' in t: return 'Các bộ lạc thảo nguyên đối địch, lều nỉ, cung tên, những cuộc phục kích và thù hận truyền đời.'
    if 'tây hạ' in t or 'nhà kim' in t or 'trung đô' in t: return 'Quân Mông Cổ vây đánh thành trì Trung Hoa phía bắc, khói lửa trước tường thành đá.'
    if 'khwarezm' in t or 'otrar' in t or 'bukhara' in t or 'samarkand' in t or 'urgench' in t: return 'Chiến dịch Khwarezm ở Trung Á, thành phố Hồi giáo, sa mạc, kỵ binh Mông Cổ và khói lửa thảm khốc.'
    if 'kurultai' in t or '1206' in t or 'yassa' in t: return 'Đại hội kurultai năm 1206, các thủ lĩnh Mông Cổ tôn Temüjin làm đại hãn dưới trời xanh vĩnh cửu.'
    if 'yam' in t or 'thương mại' in t or 'tôn giáo' in t: return 'Đế chế Mông Cổ vận hành bằng trạm dịch yam, thương nhân, sứ giả và người đưa tin vượt thảo nguyên.'
    return f'Cảnh lịch sử Mông Cổ thế kỷ XII-XIII, thảo nguyên rộng, lều nỉ, kỵ binh, cung composite, quyền lực bộ lạc và chiến tranh, minh họa đoạn {section}.'

def build_scenes(title, series, body):
    scenes = []; sid = 1; section = 'Mở đầu'
    for block in [b.strip() for b in body.split('\n\n') if b.strip()]:
        m = re.match(r'^(\d+)\.\s+(.+)$', block)
        if m:
            section = SECTION_TITLES.get(m.group(1), m.group(2).strip())
            scenes.append({'scene_id': f'scene_{sid:03d}', 'type': 'heading', 'section': section, 'summary': section, 'narration': section, 'text': section, 'visual_beat': f'Title-card heading for section: {section}', 'visual_prompt': f'Vietnamese old-calligraphy title-card on warm antique paper: {section}', 'estimated_seconds': 4, 'rule': 'heading_must_be_own_scene'})
            sid += 1; continue
        for chunk in chunk_para(block):
            vb = visual_for(chunk, section)
            scenes.append({'scene_id': f'scene_{sid:03d}', 'type': 'visual_beat', 'section': section, 'summary': chunk[:140] + ('...' if len(chunk) > 140 else ''), 'narration': chunk, 'text': chunk, 'visual_beat': vb, 'visual_prompt': vb + ' 16:9 horizontal historical storytelling image, ink-wash window centered on warm antique paper, Mongol steppe epic feeling, historically grounded 12th-13th century Inner Asian / Mongol Empire world.', 'estimated_seconds': max(6, min(12, round(len(chunk) / 34))), 'rule': 'split_by_visual_beat_8_12_seconds'})
            sid += 1
    return scenes

def character_bible():
    chars = [
        ('Temüjin / Thành Cát Tư Hãn','founder of the Mongol Empire','40s-60s','Gương mặt rắn rỏi, mắt sâu, râu ria vừa phải, khí chất lạnh và quyết đoán','Áo choàng da lông và giáp lamellar Mông Cổ thế kỷ XIII, mũ lông, cung composite','ý chí sắt, thực dụng, lãnh đạo bẩm sinh','Thảo nguyên Mông Cổ thế kỷ XII-XIII, thống nhất các bộ lạc và chinh phục Á Âu','temujin, chinggis khan, genghis khan, mongol khan','Không vẽ như hoàng đế Trung Hoa hoặc chiến binh fantasy'),
        ('Hoelun','mother of Temüjin','40s-60s','Người mẹ Mông Cổ mạnh mẽ, khuôn mặt từng trải, ánh mắt bảo vệ con','Trang phục phụ nữ du mục Mông Cổ, áo choàng dày, khăn/mũ lông','kiên cường, chịu đựng, bảo bọc','Gia đình Borjigin bị bỏ rơi sau cái chết của Yesügei','hoelun, mongol mother, borjigin family','Không hiện đại hóa'),
        ('Börte','wife and political partner of Temüjin','20s-40s','Phụ nữ quý tộc Mông Cổ điềm tĩnh, ánh mắt thông minh','Trang phục Onggirat/Mông Cổ quý tộc, áo choàng thêu, trang sức vừa phải','bền bỉ, chính trị, danh dự','Liên minh hôn nhân, vụ Merkit bắt cóc, gia đình đại hãn','borte, ongirat, mongol noble woman','Không vẽ như công chúa Trung Hoa'),
        ('Jamukha','anda and rival of Temüjin','30s-40s','Quý tộc thảo nguyên uy nghi, nét mặt kiêu hãnh và bi kịch','Giáp và áo choàng thủ lĩnh Mông Cổ, cờ bộ lạc','kiêu hãnh, quý tộc, đối đầu','Trật tự quý tộc thảo nguyên đối lập với hệ thống công trạng của Temüjin','jamukha, anda, mongol rival','Không biến thành phản diện fantasy'),
        ('Toghrul / Vương Hãn','Kereit ally and khan','50s-60s','Thủ lĩnh lớn tuổi quyền uy, râu dài, dáng vẻ từng trải','Trang phục thủ lĩnh Kereit, áo choàng nặng, mũ lông','thực dụng, liên minh, quyền lực','Liên minh Kereit hỗ trợ Temüjin chống Merkit','toghrul, wang khan, kereit','Không dùng trang phục Trung Hoa'),
        ('Jebe và Subutai','Mongol generals','30s-50s','Các tướng kỵ binh Mông Cổ sắc bén, nét mặt tập trung','Giáp da/lamellar, cung composite, bản đồ chiến dịch','táo bạo, mưu lược, thần tốc','Chiến dịch thảo nguyên, Khwarezm, Kavkaz và sông Kalka','jebe, subutai, mongol generals','Không vẽ như samurai hoặc hiệp sĩ châu Âu'),
        ('Kỵ binh Mông Cổ','collective Mongol cavalry','mixed ages','Đội kỵ binh cơ động, cung thủ trên lưng ngựa, bụi cuộn sau vó ngựa','Áo da, giáp lamellar nhẹ, mũ lông, cung composite','kỷ luật, nhanh, đáng sợ','Cỗ máy chiến tranh Mông Cổ thế kỷ XIII','mongol cavalry, horse archers, steppe army','Không dùng súng, không giáp plate châu Âu'),
        ('Đối thủ và thành thị bị chinh phục','Tatar, Merkit, Jin, Xi Xia, Khwarezm cities','mixed ages','Thủ lĩnh bộ lạc, quân thành trì, dân đô thị Trung Á và Hoa Bắc trong khói lửa','Trang phục đa dạng: thảo nguyên, Tây Hạ, Kim, Trung Á Hồi giáo','sợ hãi, chống cự, hỗn loạn','Các cuộc chinh phạt từ Đông Á đến Trung Á','tatar, merkit, jin, khwarezm, samarkand','Phân biệt văn hóa, không trộn lẫn vô căn cứ'),
    ]
    return {'project_id': PROJECT.name, 'visual_style_global': '16:9 horizontal historical storytelling, ink-wash window centered on warm antique paper, muted sepia/charcoal/bronze, Mongol steppe epic, historically grounded 12th-13th century Inner Asia and Mongol Empire.', 'consistency_rules': ['Preserve Mongol steppe clothing, felt tents, horse archers, composite bows, lamellar/leather armor, and Inner Asian geography.', 'Use character refs before scenes where recurring figures appear clearly.', 'Avoid modern objects, fantasy armor, Chinese emperor drift unless depicting Jin/Xi Xia cities, European plate armor, samurai styling, and anachronistic firearms.'], 'characters': [{'name':n,'role':r,'estimated_age':a,'appearance':ap,'costume':c,'temperament':te,'historical_cultural_context':h,'visual_keywords':vk,'negative_notes':neg} for n,r,a,ap,c,te,h,vk,neg in chars]}

def checklist_update(done_upto, current):
    path = PROJECT / 'checklist_19_steps.json'; doc = json.loads(path.read_text(encoding='utf-8'))
    doc['current_step'] = current; doc['updated_at'] = NOW()
    for st in doc['steps']:
        if st['step'] <= done_upto: st['status'] = 'completed'; st['completed_at'] = st.get('completed_at') or NOW()
        elif st['step'] == current: st['status'] = 'in_progress'; st['completed_at'] = None
        else: st['status'] = 'pending'; st['completed_at'] = None
    path.write_text(json.dumps(doc, ensure_ascii=False, indent=2), encoding='utf-8')
    lines = ['# Checklist 19 Steps', '', f"Project ID: {doc['project_id']}", f"Channel: {doc['channel']}", f"Current step: {doc['current_step']}", f"Updated at: {doc['updated_at']}", '']
    for st in doc['steps']:
        box = '[x]' if st['status'] == 'completed' else '[ ]'
        lines += [f"## {box} Step {st['step']:02d} - {st['name']}", '', f"Status: {st['status']}", '']
    (PROJECT / 'checklist_19_steps.md').write_text('\n'.join(lines), encoding='utf-8')

def main():
    clean = clean_text(SOURCE.read_text(encoding='utf-8')); CLEAN.write_text(clean, encoding='utf-8')
    title, series, body = extract_meta(clean); scenes = build_scenes(title, series, body)
    meta = {'project_id': PROJECT.name, 'title': title, 'series': series, 'target_beat_seconds': '8-12', 'split_rule': 'Headings are separate scenes; passages split by visual beats, not metadata.', 'total_scenes': len(scenes), 'scenes': scenes}
    SCENES.write_text(json.dumps(meta, ensure_ascii=False, indent=2), encoding='utf-8')
    heads = [s for s in scenes if s['type'] == 'heading']; visuals = [s for s in scenes if s['type'] == 'visual_beat']
    vals = {'project_id': PROJECT.name, 'step': 2, 'status': 'completed', 'title': title, 'series': series, 'total_scenes': len(scenes), 'heading_scenes': len(heads), 'visual_beat_scenes': len(visuals), 'metadata_excluded_from_scenes': True, 'duplicate_narration_count': len(scenes)-len({s['narration'] for s in scenes}), 'long_scene_count': sum(1 for s in visuals if len(s['narration']) > 520)}
    vals['valid'] = vals['duplicate_narration_count'] == 0 and vals['long_scene_count'] == 0 and len(heads) == 11
    SCENE_VAL.write_text(json.dumps(vals, ensure_ascii=False, indent=2), encoding='utf-8')
    (PROJECT / 'logs/step02_manifest.json').write_text(json.dumps({'step':2,'status':'completed','output':str(CLEAN)}, ensure_ascii=False, indent=2), encoding='utf-8')
    checklist_update(2, 3)
    BIBLE.write_text(json.dumps(character_bible(), ensure_ascii=False, indent=2), encoding='utf-8')
    (PROJECT / 'logs/step03_manifest.json').write_text(json.dumps({'step':3,'status':'completed','output':str(BIBLE)}, ensure_ascii=False, indent=2), encoding='utf-8')
    checklist_update(3, 4)
    STEP4.write_text(json.dumps({'project_id': PROJECT.name, 'step': 4, 'status': 'completed_approved_for_next_step', 'approved_for_next_step': True, 'total_scenes': len(scenes), 'heading_scenes': len(heads), 'visual_beat_scenes': len(visuals), 'valid_for_review': vals['valid']}, ensure_ascii=False, indent=2), encoding='utf-8')
    (PROJECT / 'logs/step04_manifest.json').write_text(json.dumps({'step':4,'status':'completed','output':str(STEP4)}, ensure_ascii=False, indent=2), encoding='utf-8')
    checklist_update(4, 5)
    print(json.dumps({'project_id': PROJECT.name, 'title': title, 'series': series, 'total_scenes': len(scenes), 'headings': len(heads), 'visual_beats': len(visuals), 'valid': vals['valid']}, ensure_ascii=False))
if __name__ == '__main__': main()
