#!/usr/bin/env python3
from __future__ import annotations

import datetime
import json
import os
import subprocess
import sys
from pathlib import Path

PROJECT = Path('/data/video-pipeline/Youtube-Video-Maker/Project/28052026-001-Tay-Son-That-Ho-Tuong')
GEN_SCRIPT = Path('/home/hermes/.hermes/skills/media/gen-img-gpt/scripts/gen_img_gpt.py')
PLAN_PATH = PROJECT / 'script/character_reference_plan.json'
BIBLE_PATH = PROJECT / 'script/character_bible.json'
LOG_PATH = PROJECT / 'logs/step_05_character_refs_run.log'
MANIFEST_PATH = PROJECT / 'logs/step_05_character_refs_manifest.json'

STYLE = (
    'Vietnamese historical ink-and-watercolor character reference, 16:9 horizontal landscape canvas, '
    'sepia parchment concept sheet, warm antique cream paper, soft brown-black ink linework, transparent watercolor wash, '
    'muted earth tones, handcrafted Vietnamese co phong historical feeling, historically grounded Tay Son era clothing and weapons.'
)
NEGATIVE = (
    'No modern objects, no Western fantasy armor, no Chinese imperial dragon robe, no text labels, no watermark, '
    'no logo, no vertical canvas, no square canvas, no 3D/game key art, no glossy poster, no anime.'
)

PROMPT_HINTS = {
    'nguyen-hue-quang-trung': 'charismatic Vietnamese warrior-king in simple ao vai cloth and battle-ready Tay Son command attire, red Tay Son banner atmosphere, decisive eyes, strong but lean build, leader aura, saber at waist',
    'vo-van-dung': 'Tay Son tiger general, stern loyal expression, Binh Dinh martial bearing, practical lamellar armor over brown cloth, Vietnamese sword, weathered battlefield dignity',
    'tran-quang-dieu': 'disciplined Tay Son general, composed strategic face, Quy Nhon citadel defender mood, neat armor, command baton or sword, calm authority',
    'nguyen-van-tuyet': 'Tay Son cavalry commander, night march spirit, horseman cloak, agile build, focused eyes, saber and travel-worn armor',
    'le-van-hung': 'Tay Son hổ tướng with broad blade thanh dao, firelit battlefield mood, sturdy posture, rugged armor, loyal and fierce temperament',
    'ly-van-buu': 'Binh Dinh martial artist general, võ đường earth courtyard feeling, strong stance, staff or traditional weapon, grounded folk hero aura',
    'nguyen-van-loc': 'Tay Son field officer among ranks, disciplined soldier-general, practical armor, spear or sword, steady expression, army formation mood',
    'vo-dinh-tu': 'Tay Son martial master, roi quyền staff-fighting identity, compact powerful stance, traditional Vietnamese warrior clothing, intense eyes',
    'bui-thi-xuan': 'Vietnamese female Tay Son general, dignified and fierce, elephant corps commander aura, historically grounded armor over ao, hair tied neatly, commanding gaze',
    'nguyen-anh': 'Nguyen Anh as rival ruler in exile and war, noble but tense expression, southern Vietnamese royal military attire, guarded cautious mood, not heroic protagonist',
}


def log(msg: str) -> None:
    LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
    stamp = datetime.datetime.now().isoformat(timespec='seconds')
    line = f'{stamp} {msg}'
    print(line, flush=True)
    with LOG_PATH.open('a', encoding='utf-8') as f:
        f.write(line + '\n')


def load_env_file() -> None:
    env_path = Path('/home/hermes/.hermes/.env')
    if not env_path.exists():
        return
    for raw in env_path.read_text(encoding='utf-8').splitlines():
        line = raw.strip()
        if not line or line.startswith('#') or '=' not in line:
            continue
        k, v = line.split('=', 1)
        os.environ.setdefault(k, v)


def build_prompt(item: dict, bible_by_name: dict) -> str:
    name = item['name']
    slug = item['slug']
    c = bible_by_name.get(name, {})
    details = [
        f'Character: {name}.',
        f'Role: {item.get("role") or c.get("role", "Tay Son historical character")}.',
        f'Identity keywords: {item.get("prompt_summary", "")}.',
        f'Appearance: {c.get("appearance", "Vietnamese historical facial features, grounded, not fantasy")}.',
        f'Costume: {c.get("costume", "Tay Son era Vietnamese warrior clothing and practical armor")}.',
        f'Temperament: {c.get("temperament", "solemn historical dignity")}.',
        f'Specific visual direction: {PROMPT_HINTS.get(slug, "grounded Tay Son era Vietnamese historical figure")}.',
    ]
    return '\n'.join([
        STYLE,
        'Create a character reference image, not a final story scene.',
        'Composition: one dominant full-body pose plus two small bust/face details and subtle costume/weapon detail vignettes, all on a single horizontal 16:9 parchment sheet with breathing room.',
        'Keep face, age, hair, costume language, weapon, and aura clear for later scene identity consistency.',
        *details,
        NEGATIVE,
    ])


def main() -> int:
    load_env_file()
    if not os.environ.get('GEN_IMG_API_KEY'):
        log('BLOCKED missing GEN_IMG_API_KEY')
        return 2
    if not GEN_SCRIPT.exists():
        log(f'BLOCKED missing gen script: {GEN_SCRIPT}')
        return 2
    plan = json.loads(PLAN_PATH.read_text(encoding='utf-8'))
    bible = json.loads(BIBLE_PATH.read_text(encoding='utf-8'))
    bible_by_name = {c.get('name'): c for c in bible.get('characters', [])}
    results = []
    total = len(plan.get('items', []))
    for idx, item in enumerate(plan.get('items', []), 1):
        out = Path(item['output_path'])
        out.parent.mkdir(parents=True, exist_ok=True)
        if out.exists() and out.stat().st_size > 1000:
            item['status'] = 'done'
            item['generated_at'] = item.get('generated_at') or datetime.datetime.now().isoformat(timespec='seconds')
            results.append({'name': item['name'], 'status': 'exists', 'output_path': str(out)})
            log(f'skip existing {idx}/{total} {item["name"]} -> {out}')
            continue
        prompt = build_prompt(item, bible_by_name)
        item['prompt'] = prompt
        log(f'generate {idx}/{total} {item["name"]} -> {out}')
        cmd = [sys.executable, str(GEN_SCRIPT), '--prompt', prompt, '--output', str(out), '--size', 'auto', '--quality', 'auto', '--background', 'auto', '--image-detail', 'high', '--output-format', 'png']
        started = datetime.datetime.now().isoformat(timespec='seconds')
        proc = subprocess.run(cmd, cwd=str(PROJECT), text=True, capture_output=True, timeout=420)
        if proc.returncode == 0 and out.exists() and out.stat().st_size > 1000:
            item['status'] = 'done'
            item['generated_at'] = datetime.datetime.now().isoformat(timespec='seconds')
            item['output_path'] = str(out)
            results.append({'name': item['name'], 'status': 'done', 'output_path': str(out), 'started_at': started})
            log(f'done {item["name"]} bytes={out.stat().st_size}')
        else:
            item['status'] = 'failed'
            item['error'] = (proc.stderr or proc.stdout or 'unknown error')[-2000:]
            results.append({'name': item['name'], 'status': 'failed', 'output_path': str(out), 'error': item['error']})
            log(f'failed {item["name"]}: {item["error"]}')
            PLAN_PATH.write_text(json.dumps(plan, ensure_ascii=False, indent=2), encoding='utf-8')
            break
        PLAN_PATH.write_text(json.dumps(plan, ensure_ascii=False, indent=2), encoding='utf-8')
    done = sum(1 for x in plan.get('items', []) if x.get('status') == 'done')
    failed = [x for x in plan.get('items', []) if x.get('status') == 'failed']
    plan['status'] = 'completed_pending_user_approval' if done == total and not failed else 'partial_failed'
    plan['updated_at'] = datetime.datetime.now().isoformat(timespec='seconds')
    plan['done_count'] = done
    plan['total_count'] = total
    PLAN_PATH.write_text(json.dumps(plan, ensure_ascii=False, indent=2), encoding='utf-8')
    manifest = {
        'project_id': PROJECT.name,
        'step': 5,
        'status': plan['status'],
        'provider': 'gen-img-gpt',
        'model': 'cx/gpt-5.5-image',
        'done': done,
        'total': total,
        'results': results,
        'plan_path': str(PLAN_PATH),
    }
    MANIFEST_PATH.parent.mkdir(parents=True, exist_ok=True)
    MANIFEST_PATH.write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding='utf-8')
    log('DONE ' + json.dumps({'status': plan['status'], 'done': done, 'total': total}, ensure_ascii=False))
    return 0 if done == total and not failed else 1


if __name__ == '__main__':
    raise SystemExit(main())
