"""Expand video plans into shoot/generation scripts and accessibility source text."""

from __future__ import annotations

import json
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1] / "content" / "speech"
PLAN = json.loads((ROOT / "plan" / "content-plan.json").read_text(encoding="utf-8"))


def concept_path(c: dict) -> Path:
    return ROOT / "concepts" / Path(*c["category"].split(".")) / c["key"]


def action_direction(kind: str, depiction: str, purpose: str) -> str:
    if kind in {"action", "routine"}:
        return f"Perform the action once at a natural, slightly deliberate pace: {depiction}. Begin and end in stable states."
    if kind in {"social_word", "emotion"}:
        return f"Let the context motivate the response naturally: {depiction}. Use congruent face, body and partner response without exaggeration."
    if kind in {"attribute", "preposition", "quantity"}:
        return f"Change or reveal only the target comparison: {depiction}. Keep non-target colour, size, position and lighting matched."
    if kind in {"person", "location"}:
        return f"Show ordinary role or place evidence through natural activity: {depiction}. Do not rely on a costume, sign or stereotype."
    return f"Reveal and interact with the target once: {depiction}. Keep its identity, scale and appearance constant."


def sound_caption(kind: str, depiction: str) -> str | None:
    if kind in {"action", "routine"}: return "[Quiet sounds of the activity]"
    if kind in {"social_word"}: return "[Quiet everyday room ambience]"
    return None


updated = 0
for concept in PLAN["concepts"]:
    base = concept_path(concept)
    video_dir = base / "videos"
    plan = json.loads((video_dir / "video-generation-plan.json").read_text(encoding="utf-8"))
    scripts = []
    for video in plan["videos"]:
        setup = video["setup"]
        depiction = setup["depiction"]
        caption = sound_caption(concept["type"], depiction)
        timeline = [
            {"timecode":"00:00.000-00:01.500","picture":video["story_beats"][0]["story_beat"],"performance":"Settle naturally; no one looks at camera unless the communicative meaning requires partner gaze.","camera":"Locked establishing view; level horizon; no handheld shake.","audio":"Capture clean room tone only.","edit":"Start from a clean stable frame; no title card."},
            {"timecode":"00:01.500-00:05.500","picture":video["story_beats"][1]["story_beat"],"performance":action_direction(concept["type"],depiction,video["purpose"]),"camera":"Medium-close teaching view; keep the target, relevant hands, reference object and face in focus.","audio":"Natural production sound at low level; no spoken label is required.","edit":"One continuous take preferred; one motivated straight cut permitted."},
            {"timecode":"00:05.500-00:08.000","picture":video["story_beats"][2]["story_beat"],"performance":"Hold the completed meaning without pointing, celebration or repeated action.","camera":"Locked result frame with at least 5 percent safe margin around the target.","audio":"Continue matching room tone; fade neither picture nor sound before 8 seconds.","edit":"End on a stable frame suitable for pausing."},
        ]
        ad = f"In {setup['setting']}, {depiction}."
        caption_cues = [] if caption is None else [{"start":"00:01.500","end":"00:05.500","text":caption}]
        scripts.append({
            "video_key":video["video_key"],"purpose":video["purpose"],"duration_seconds":8,"format":"16:9 landscape","frame_rate":"25 or 30 fps constant","resolution":"1920x1080 minimum",
            "reference_setup_image":f"{video['video_key']}_setup_01.png","reference_concept_image":video["reference_image"]["filename"],
            "story_summary":video["narrative_intent"],"setting":setup["setting"],"people":setup["people"],"target_depiction":depiction,
            "timeline":timeline,
            "camera_direction":{"lens_equivalent":"35-50 mm natural perspective; 70-85 mm for isolated face detail","height":"target or seated eye level","movement":"locked-off; optional imperceptibly slow push-in","focus":"sufficient depth of field to keep semantic evidence sharp","exposure":"natural skin tones and no clipped highlights","white_balance":"locked throughout","continuity_locks":setup["continuity_lock"]},
            "performance_direction":{"primary":action_direction(concept["type"],depiction,video["purpose"]),"pace":"natural and readable, never slow-motion acting","gaze":"natural task or partner gaze; never require eye contact","repetitions":"one complete target event","safeguarding":"stop on discomfort; no forced compliance or distress performance"},
            "spoken_script":{"required":False,"dialogue":[],"localized_voiceover_optional":f"Use the approved localized semantic label or instruction for {concept['key']} only after SLP and native review.","record_separately":True},
            "caption_script":{"language":"en","picture_only_policy":"Do not caption visual action as sound. Leave captions off when the final mix contains no speech or meaningful sound.","cues":caption_cues,"localized_caption_requirement":"Create from the locked final audio, not by translating this draft blindly."},
            "audio_description":{"language":"en","script":ad,"delivery":"neutral concise present tense; place in a natural pause or provide as separate accessible track","review":"SLP and blind/low-vision accessibility review required"},
            "accessibility_notes":["Meaning must be understandable with audio muted.","Do not use speech, music, colour, camera movement or a transient cue as the only signal.","Keep target visible for at least two seconds after the event.","No flash, flicker, rapid cut, whip pan or unexpected loud sound.","Provide player pause, replay, captions toggle and audio-description track where supported.","Check target contrast and visibility at 320-pixel-wide playback."],
            "generation_prompt":video["generation_prompt"],"negative_prompt":"; ".join(video["negative_constraints"]),
            "generation_settings":{"aspect_ratio":"16:9","duration_seconds":8,"camera_motion":"locked","seed":"record exact provider seed when available","reference_strength":"preserve semantics and composition; do not clone a real identity","generate_audio":False},
            "continuity_review":["same participant identity and anatomy","same clothing and assistive devices","same target object and quantity","hands remain anatomically plausible","background objects do not appear or disappear","action begins and ends logically"],
            "delivery":{"video_filename":f"{video['video_key']}.mp4","caption_filename":f"{video['video_key']}.captions.<language>.vtt","audio_description_filename":f"{video['video_key']}.audio_description.<language>.wav","poster_filename":f"{video['video_key']}.poster.png"},
            "status":"script_ready_review_required"
        })
    record={"schema_version":1,"concept_key":concept["key"],"video_count":len(scripts),"scripts":scripts,"approval":{"clinical":False,"cultural":False,"accessibility":False,"rights":False,"technical":False}}
    out=video_dir/"video-production-script.json"; text=json.dumps(record,ensure_ascii=False,indent=2)+"\n"
    if not out.exists() or out.read_text(encoding="utf-8")!=text: out.write_text(text,encoding="utf-8"); updated+=1
print(f"Video production scripts ready: {len(PLAN['concepts'])*3} videos; {updated} files updated")
