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

PROJECT = Path('/data/video-pipeline/Youtube-Video-Maker/Project/18062026-001-Nha-Ho')
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': 'Một triều đại ngắn ngủi nhưng không thể bỏ qua',
    '2': 'Cuối triều Trần - khi hào khí Đông A chỉ còn là ký ức',
    '3': 'Hồ Quý Ly - người nhìn thấy căn bệnh của thời cuộc',
    '4': 'Năm 1400 - nhà Hồ thay nhà Trần',
    '5': 'Những cải cách táo bạo của nhà Hồ',
    '6': 'Thành Tây Đô - giấc mộng phòng thủ bằng đá',
    '7': 'Nhà Minh và cái cớ phù Trần diệt Hồ',
    '8': 'Cuộc kháng chiến chống Minh 1406 - 1407',
    '9': 'Hai mươi năm đau thương sau sự sụp đổ',
    '10': 'Hồ Quý Ly - tội nhân, nhà cải cách, hay bi kịch lịch sử',
    '11': 'Di sản của nhà Hồ',
    '12': 'Bảy năm và một câu hỏi lớn của lịch sử',
}

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 'hồ quý ly' in t: return 'Hồ Quý Ly trong triều phục cuối Trần đầu Hồ, gương mặt quyết đoán giữa bản đồ cải cách và bóng giặc phương Bắc.'
    if 'hồ hán thương' in t: return 'Hồ Hán Thương trên ngai triều Hồ, trẻ hơn cha, đứng giữa áp lực chính danh và chiến tranh.'
    if 'thành tây đô' in t or 'thành nhà hồ' in t: return 'Thành Tây Đô bằng đá khổng lồ ở Thanh Hóa, tường thành uy nghi dưới trời giông bão thế kỷ XV.'
    if 'tiền giấy' in t or 'thông bảo hội sao' in t: return 'Quan lại nhà Hồ ban hành tiền giấy Thông bảo hội sao giữa chợ dân gian đầy ngờ vực.'
    if 'hạn điền' in t or 'hạn nô' in t: return 'Cảnh đo đạc ruộng đất, sổ bộ và quan lại nhà Hồ thực thi hạn điền hạn nô trong làng quê Đại Ngu.'
    if 'quân minh' in t or 'nhà minh' in t or 'phù trần diệt hồ' in t: return 'Đại quân Minh phương Bắc với cờ xí dày đặc tiến xuống Đại Ngu, danh nghĩa phù Trần diệt Hồ che giấu tham vọng xâm lược.'
    if 'chu văn an' in t: return 'Chu Văn An rời triều đình cuối Trần, dáng người thanh liêm cô độc giữa sân điện u ám.'
    if 'chế bồng nga' in t or 'chiêm thành' in t: return 'Quân Chiêm Thành thời Chế Bồng Nga uy hiếp Thăng Long, khói lửa phủ lên kinh đô cuối Trần.'
    if 'lê lợi' in t or 'lam sơn' in t: return 'Ngọn cờ Lam Sơn le lói trong rừng núi, báo trước cuộc phục hưng sau những năm Bắc thuộc.'
    if 'nhà trần' in t or 'đông a' in t: return 'Triều đình cuối Trần suy tàn, hào khí Đông A chỉ còn vang vọng trong ký ức và thư tịch cũ.'
    return f'Cảnh lịch sử Đại Việt - Đại Ngu thời nhà Hồ, không khí cải cách, chính biến và giông bão mất nước, 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, Vietnamese co phong feeling, historically grounded early 15th-century Đại Việt / Đại Ngu / Nhà Hồ.', '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 = [
        ('Hồ Quý Ly','founder of Hồ dynasty and reformer','60s-70s','Sắc mặt cứng rắn, ánh mắt sắc bén, khí chất quyền lực và cô độc','Triều phục Đại Ngu đầu thế kỷ XV, mũ quan/vua giản lược, màu trầm','quyết đoán, cải cách, nghiêm khắc','Cuối Trần - nhà Hồ, cải cách và kháng Minh','ho quy ly, nha ho, dai ngu, reformer','Không vẽ như pháp sư hoặc bạo chúa fantasy'),
        ('Hồ Hán Thương','emperor of Hồ dynasty','30s-40s','Gương mặt trẻ hơn Hồ Quý Ly, chịu áp lực ngai vàng','Long bào/triều phục Đại Ngu, trang nghiêm nhưng nặng nề','lo lắng, cố giữ triều đại','Nhà Hồ 1401-1407, kháng Minh','ho han thuong, dai ngu emperor','Không nhầm với hoàng đế Trung Hoa'),
        ('Trần Thiếu Đế và tôn thất nhà Trần','deposed Trần royal line','child to elder','Dáng vẻ vương triều cũ suy tàn, buồn bã','Triều phục cuối Trần, màu phai, ít xa hoa','bất lực, tiếc nuối, mất quyền','Chính biến năm 1400','tran thieu de, late tran dynasty','Không hiện đại hóa'),
        ('Chu Văn An','upright Confucian teacher','elder','Râu tóc bạc, thần thái thanh liêm, ánh mắt nghiêm nghị','Áo nho sĩ giản dị, mũ nhà nho','chính trực, can gián, thất vọng','Cuối Trần, Thất trảm sớ','chu van an, confucian teacher','Không vẽ như quan võ'),
        ('Quân dân Đại Ngu','collective soldiers and common people','mixed ages','Binh sĩ, dân phu, nông dân mệt mỏi nhưng vẫn trong bão lịch sử','Trang phục dân binh Đại Việt đầu thế kỷ XV, giáo mác, áo vải','chịu đựng, phân vân, kháng cự rời rạc','Nhà Hồ cải cách và kháng Minh','dai ngu soldiers, vietnamese villagers','Không dùng đồ hiện đại'),
        ('Quân Minh','Ming invading army','mixed ages','Đội quân phương Bắc đông đảo, kỷ luật, cờ xí dày đặc','Giáp quân Minh đầu thế kỷ XV, cờ đỏ vàng, vũ khí bộ binh','áp đảo, xâm lược, mưu lược','Chiến tranh Minh - Hồ 1406-1407','ming army, early 15th century','Không biến thành quái vật'),
        ('Chế Bồng Nga và quân Chiêm Thành','Champa threat late Trần','40s','Vua Chiêm/chiến binh Chăm uy nghi trong chiến dịch đánh Thăng Long','Trang phục Chăm cuối thế kỷ XIV, hoa văn bản địa','mạnh mẽ, uy hiếp','Chiêm Thành dưới Chế Bồng Nga','che bong nga, champa army','Không nhầm với Khmer hoặc Trung Hoa'),
        ('Lê Lợi và nghĩa quân Lam Sơn','future liberation movement','30s-40s','Thủ lĩnh Lam Sơn trong rừng núi, khí chất kiên định','Áo chiến binh Lam Sơn giản dị, gươm, cờ nghĩa quân','kiên trì, phục quốc','Khởi nghĩa Lam Sơn sau Bắc thuộc Minh','le loi, lam son uprising','Chỉ xuất hiện như dự báo cuối truyện'),
    ]
    return {'project_id': PROJECT.name, 'visual_style_global': '16:9 horizontal historical storytelling, ink-wash window centered on warm antique paper, muted sepia/charcoal Vietnamese co phong, historically grounded late 14th to early 15th century Đại Việt / Đại Ngu / Nhà Hồ.', 'consistency_rules': ['Preserve Vietnamese early 15th-century clothing, weapons, architecture, Thanh Hóa Tây Đô stone citadel, and late Trần / Hồ atmosphere.', 'Use character refs before scenes where recurring figures appear clearly.', 'Avoid modern objects, fantasy armor, anachronistic Chinese palace drift unless showing Ming invaders.'], '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) == 12
    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()
