import type { GameState } from '@rpg/engine';
import type { ItemId } from '../data/items';
import { usedFlag } from '../data/ids';
import {
    resolveInteractable,
    type InteractableDef,
    type InteractCtx,
    type InteractPick,
    type InteractResponse
} from '../data/interactables';

/**
 * Рантайм интерактивных объектов области: поиск по тайлу, флаги used:<id>,
 * применение эффектов реакции через sink (сцена подставляет сумку/звук/тост).
 */

/** Эффекты реакции — мост между чистым резолвером и игрой. */
export interface InteractSink {
    hasItem(id: ItemId): boolean;
    give(id: ItemId, count: number): void;
    playSound(key: string): void;
    showToast(text: string): void;
    /** Сюжетный эффект реакции (DIALOGUE_CUSTOM): сцена запускает катсцену. */
    onCustom?(name: string): void;
    /**
     * Одноразовый объект «поднят» (once, реакция ok): сцена убирает вьюху с
     * поля (fade) и даёт визуальный отклик. Вызывается после apply.
     */
    onConsumed?(def: InteractableDef, response: InteractResponse): void;
}

export class Interactables {
    constructor(
        private defs: InteractableDef[],
        private state: GameState,
        private sink: InteractSink
    ) {}

    /** Объект на тайле (или null). */
    defAt(x: number, y: number): InteractableDef | null {
        return this.defs.find((d) => d.tile.x === x && d.tile.y === y) ?? null;
    }

    /** Одноразовый объект уже использован (флаг used:<id> в GameState). */
    isUsed(id: string): boolean {
        return this.state.hasFlag(usedFlag(id));
    }

    /** Взаимодействие: выбор реакции + применение эффектов. */
    tryInteract(def: InteractableDef): InteractPick {
        const ctx: InteractCtx = {
            hasFlag: (f) => this.state.hasFlag(f),
            hasItem: (i) => this.sink.hasItem(i),
            getVar: (id) => this.state.getNumber(id),
            used: (id) => this.isUsed(id)
        };
        const pick = resolveInteractable(def, ctx);
        if (pick?.ok) {
            this.apply(def, pick.response);
            if (def.once) this.sink.onConsumed?.(def, pick.response);
        }
        return pick;
    }

    /** Применить эффекты реакции (порядок: флаги -> вар -> предмет -> звук -> тост). */
    private apply(def: InteractableDef, r: InteractResponse): void {
        for (const f of r.setFlags ?? []) this.state.setFlag(f);
        for (const f of r.clearFlags ?? []) this.state.clearFlag(f);
        if (r.setVar) this.state.setVar(r.setVar.id, r.setVar.value);
        if (r.addVar) this.state.setVar(r.addVar.id, this.state.getNumber(r.addVar.id) + r.addVar.by);
        if (r.gives) this.sink.give(r.gives, r.count ?? 1);
        if (def.once) this.state.setFlag(usedFlag(def.id));
        if (r.sound) this.sink.playSound(r.sound);
        if (r.custom) this.sink.onCustom?.(r.custom);
        if (r.toast) {
            // Тост с прогрессом (как у цветов): «Тон (2/3)».
            const n = r.toast.varKey !== undefined ? this.state.getNumber(r.toast.varKey) : null;
            const progress =
                n !== null && r.toast.goal !== undefined ? ` (${Math.min(n, r.toast.goal)}/${r.toast.goal})` : '';
            this.sink.showToast(r.toast.text + progress);
        } else {
            this.sink.showToast(r.text);
        }
    }
}