import {
    Container,
    DialogueBox,
    DialogueRunner,
    GameState,
    type DialogueBoxOptions,
    type DialogueGraph
} from '@rpg/engine';
import { applyDialogueEffect, type DialogueEffectSink } from './DialogueEffects';

/**
 * Диалоги игры: DialogueRunner движка ходит по графам и применяет флаги
 * к GameState, DialogueBox рисует реплики и варианты, do[]-эффекты идут
 * в зарегистрированный EffectSink.
 */
export class DialogueSystem {
    private box: DialogueBox;
    private runner: DialogueRunner;
    private sink: DialogueEffectSink | null = null;

    /** Сюжетное событие «диалог завершён» — для триггеров/квестов. */
    onDialogueFinished: ((id: string) => void) | null = null;
    /** Звук на каждую новую реплику (колокольчик). */
    onLineShown: (() => void) | null = null;
    /** id стартованного диалога (для onFinish). */
    private currentId: string | null = null;

    constructor(uiRoot: Container, state: GameState, options: DialogueBoxOptions) {
        this.box = new DialogueBox({
            ...options,
            onChoice: (index) => this.runner.pick(index)
        });
        uiRoot.addChild(this.box.view);

        this.runner = new DialogueRunner(state);
        this.runner.setView({
            show: ({ speaker, text, mood, choices }) => {
                this.box.show({ speaker, text, mood, choices });
                this.onLineShown?.();
            },
            hide: () => this.box.hide()
        });
        this.runner.onEffect = (op) => {
            if (this.sink) applyDialogueEffect(op, this.sink);
        };
    }

    /** Зарегистрировать приёмник do[]-эффектов (сцена с геймплеем). */
    registerSink(sink: DialogueEffectSink): void {
        this.sink = sink;
    }

    /** Уничтожить панель диалога (exit сцены — иначе копится в uiRoot). */
    destroy(): void {
        this.box.view.destroy({ children: true });
    }

    get active(): boolean {
        return this.runner.active;
    }

    /** Начать диалог по ключу из data/dialogues.ts. */
    start(graph: DialogueGraph, id: string): void {
        this.currentId = id;
        this.runner.onFinish = () => {
            const id = this.currentId;
            this.currentId = null;
            if (id) this.onDialogueFinished?.(id);
        };
        this.runner.start(graph);
    }

    /** Клик/пробел: догнать печать → активировать вариант под курсором → дальше. */
    advance(): void {
        if (this.box.revealing) {
            this.box.skipReveal();
            return;
        }
        if (this.runner.waitingForChoice) {
            this.box.activate();
            return;
        }
        this.runner.advance();
    }

    /** Листать варианты up/down (клавиатура/геймпад). */
    moveCursor(delta: number): void {
        this.box.moveCursor(delta);
    }

    /** Печать текста; звать из update сцены. */
    update(dt: number): void {
        this.box.update(dt);
    }

    /** Выбрать показанный вариант (агентный мост; индексы — из agentState.choices). */
    pickChoice(index: number): void {
        this.runner.pick(index);
    }

    /** Выбрать вариант по тексту (агентный мост). false — такого варианта нет. */
    pickChoiceByText(text: string): boolean {
        const index = this.runner.choices.findIndex((c) => c.text === text);
        if (index < 0) return false;
        this.runner.pick(index);
        return true;
    }

    /** Состояние диалога для агентного моста (снапшот; не для логики). */
    get agentState(): {
        id: string | null;
        nodeId: string | null;
        speaker: string | null;
        text: string | null;
        mood: string | null;
        tags: string[];
        choices: string[];
        path: string[];
        waitingForChoice: boolean;
    } | null {
        if (!this.runner.active) return null;
        const node = this.runner.node;
        return {
            id: this.currentId,
            nodeId: this.runner.nodeId,
            speaker: node?.speaker ?? null,
            text: node?.text ?? null,
            mood: node?.mood ?? null,
            tags: node?.tags ?? [],
            choices: this.runner.choices.map((c) => c.text),
            path: this.runner.path,
            waitingForChoice: this.runner.waitingForChoice
        };
    }
}