import { sineInOut, type EaseFn } from '../core/easing';
import { GameState } from '../core/GameState';
import type { Camera } from '../render/Camera';
import type { Vec2 } from '../math/Vec2';

/**
 * Раннер кат-сцен: view-агностичная последовательность шагов.
 * Каждый шаг при старте выполняет свой эффект; раннер ждёт его длительность
 * (0 — шаг «параллельный», следующий стартует сразу) и переходит к следующему.
 * Сцена на время сцены ставит геймплей на паузу — раннер только сообщает `active`.
 */

export type CutsceneStep =
    /** Подождать N секунд (ничего не делая). */
    | { kind: 'wait'; seconds: number }
    /** Плавно переместить камеру (в юнитах) к точке. */
    | { kind: 'cameraMove'; x: number; y: number; seconds: number; camera: Camera }
    /** Поднять флаг прохождения (мгновенно). */
    | { kind: 'setFlag'; flag: string; state: GameState }
    /** Разовый эффект (звук, кольцо, вспышка частиц). */
    | { kind: 'burst'; fn: () => void; seconds?: number }
    /** Произвольный вызов (диалог, посадка, тост и т.п.). */
    | { kind: 'call'; fn: () => void; seconds?: number };

interface RunningStep {
    seconds: number;
    /** Движение камеры: from -> to по easing. */
    cameraMove?: { from: Vec2; to: Vec2; camera: Camera };
}

/** Плавность движения камеры по умолчанию. */
const EASE: EaseFn = sineInOut;

export class CutsceneRunner {
    private queue: CutsceneStep[] = [];
    private current: RunningStep | null = null;
    private t = 0;
    /** Вызывается, когда все шаги дошли до конца. */
    onComplete: (() => void) | null = null;

    constructor(
        /** Наблюдатель: вызывается при старте каждого шага (лог, отладка). */
        private onStep?: (step: CutsceneStep) => void
    ) {}

    /** Запустить последовательность шагов (немедленно). */
    play(steps: CutsceneStep[]): void {
        this.queue = [...steps];
        this.t = 0;
        this.next();
    }

    /** Идёт ли сейчас сцена (геймплей на паузе). */
    get active(): boolean {
        return this.current !== null;
    }

    /** Секунд прошло с начала текущего шага. */
    get time(): number {
        return this.t;
    }

    private next(): void {
        const step = this.queue.shift();
        if (!step) {
            this.current = null;
            this.onComplete?.();
            return;
        }
        this.onStep?.(step);
        switch (step.kind) {
            case 'wait':
                this.current = { seconds: step.seconds };
                break;
            case 'cameraMove':
                this.current = {
                    seconds: step.seconds,
                    cameraMove: {
                        from: { x: step.camera.x, y: step.camera.y },
                        to: { x: step.x, y: step.y },
                        camera: step.camera
                    }
                };
                break;
            case 'setFlag':
                step.state.setFlag(step.flag);
                this.current = { seconds: 0 };
                break;
            case 'burst':
            case 'call':
                step.fn();
                this.current = { seconds: step.seconds ?? 0 };
                break;
        }
        // Параллельный шаг (0 секунд) — сразу к следующему.
        if (this.current.seconds <= 0) this.next();
    }

    update(dt: number): void {
        if (!this.current) return;
        this.t += dt;
        const move = this.current.cameraMove;
        if (move) {
            const k = Math.min(1, this.t / this.current.seconds);
            const e = EASE(k);
            move.camera.x = move.from.x + (move.to.x - move.from.x) * e;
            move.camera.y = move.from.y + (move.to.y - move.from.y) * e;
        }
        if (this.t >= this.current.seconds) {
            this.t = 0;
            this.next();
        }
    }
}