/**
 * Катсцены игры как данные (сюжетные сцены трека 19): метки точек камеры
 * (тайлы v1), именованные действия и реестр сцен. Рантайм — движковый
 * CutsceneRunner; resolveCutscenes связывает данные с действиями и
 * валидирует при старте (как validateDialogue — битый каталог падает сразу).
 */
import { UNIT, GROUND_Y } from '../areas';
import { QUEST_TOWER_TILE, QUEST_TONE_TILES } from './quests';
import type { CutsceneStep } from '@rpg/engine';

/** Шаг катсцены в данных: длительность + метки действия/камеры. */
export interface CutsceneStepData {
    /** Длительность шага (сек ручного времени цикла — детерминизм гейта). */
    dur: number;
    /** Именованное действие (ключ реестра действий main.ts). */
    action?: string;
    /** Аргумент действия (текст тоста). */
    text?: string;
    /** Метка точки камеры для глайда (ключ CUTSCENE_CAMS). */
    cam?: string;
    /** Точка для действия без глайда шага (ключ CUTSCENE_CAMS). */
    at?: string;
}

/** Метки точек камеры (тайлы v1; мир — центр тайла, ноги на земле). */
export const CUTSCENE_CAMS: Record<string, { x: number; y: number }> = {
    tower: QUEST_TOWER_TILE, // башня Звенца (сдача цветов)
    clint_resonator: { x: 24, y: 16 }, // резонатор Клинта (clint_resonator)
    tone_tree: { x: QUEST_TONE_TILES[1]![0], y: QUEST_TONE_TILES[1]![1] }, // дерево-механизм рощи
    machine_mouth: { x: 13, y: 1 }, // устье печи (свет machine_mouth, проп old_machine)
};

/** Действие катсцены: точка (если задана at/cam) и аргумент (текст). */
export type CutsceneAction = (point: { x: number; z: number } | null, text: string | undefined) => void;

/** Реестр сюжетных катсцен: id → шаги в данных. */
export const CUTSCENES: Record<string, CutsceneStepData[]> = {
    /** Сдача цветов: посадка → камера к башне → зелень → финал акта 1. */
    hand_in: [
        { dur: 0.8, action: 'plant_flowers' },
        { dur: 1.2, cam: 'tower' },
        { dur: 1.2, action: 'raise_tower_green' },
        { dur: 1.0, action: 'toast', text: 'Пепел отступил у тропы. Машина дышит тише... но дышит.' },
    ],
    /** Настройка резонатора (акт 2): камера к резонатору, звон,
     *  роща отвечает (камера к прозвеневшему дереву), тост-финал квеста. */
    tune: [
        { dur: 1.0, cam: 'clint_resonator' },
        { dur: 1.0, action: 'fauna_startle', at: 'clint_resonator' },
        { dur: 1.2, cam: 'tone_tree' },
        { dur: 0.8, action: 'toast', text: 'Резонатор держит тон. Роща слышит.' },
    ],
    /** Финал акта 2 — «правильный звон»: камера спускается к устью Машины,
     *  резонатор держит тон, Машина засыпает (тост — финальный шаг). */
    machine_lull: [
        { dur: 1.2, cam: 'machine_mouth' },
        { dur: 1.0 }, // правильный звон держит
        { dur: 1.2 }, // дыхание обрывается — угольки гаснут
        { dur: 0.8, action: 'toast', text: 'Машина дышит тише. Впервые за годы — тише.' },
    ],
};

/** Мир-точка метки: центр тайла, ноги на земле (граница тайлы → воксели). */
export function cutscenePoint(label: string): [number, number, number] {
    const t = CUTSCENE_CAMS[label];
    if (!t) throw new Error(`cutscenes: неизвестная точка камеры «${label}»`);
    return [t.x * UNIT + UNIT / 2, GROUND_Y, t.y * UNIT + UNIT / 2];
}

/** Связать каталог с действиями: шаги для движкового CutsceneRunner.
 *  Неизвестная метка/действие — throw (валидация при старте). */
export function resolveCutscenes(actions: Record<string, CutsceneAction>): Record<string, CutsceneStep[]> {
    const out: Record<string, CutsceneStep[]> = {};
    for (const [id, steps] of Object.entries(CUTSCENES)) {
        if (!steps.length) throw new Error(`cutscenes: сцена «${id}» пуста`);
        out[id] = steps.map((s) => {
            if (s.cam) cutscenePoint(s.cam); // метка камеры валидна
            const act = s.action ? actions[s.action] : undefined;
            if (s.action && !act) throw new Error(`cutscenes: неизвестное действие «${s.action}» (сцена ${id})`);
            const pointLabel = s.at ?? s.cam;
            return {
                dur: s.dur,
                cam: s.cam ? cutscenePoint(s.cam) : undefined,
                fn: act
                    ? () => {
                        const p = pointLabel ? cutscenePoint(pointLabel) : null;
                        act(p ? { x: p[0], z: p[2] } : null, s.text);
                    }
                    : undefined,
            };
        });
    }
    return out;
}