import { SceneRegistry, tileToWorld, type Grid, type IsometricTileMap } from '@rpg/engine';
import type { AreaDef } from '../data/locations';
import type { NpcDef } from '../data/npcs';
import type { InteractableDef } from '../data/interactables';

/**
 * Объекты сцены на движковом SceneRegistry: единый список «кто на карте»
 * (NPC, интерактивы, пропы, фауна, враги) с индексом по тайлам.
 * Клик-роутинг, подписи и телесные коллизии спрашивают здесь, а не перебирают
 * параллельные списки. Роды задаёт игра: 'npc' | 'interactable' | 'prop' |
 * 'fauna' | 'enemy'.
 */

/** Радиус тела NPC (юниты) — для будущих телесных коллизий. */
const NPC_RADIUS = 0.35;

export class SceneObjects {
    readonly registry = new SceneRegistry();

    constructor(area: AreaDef, private map: IsometricTileMap) {
        for (const def of area.npcs) {
            this.registry.add({
                id: `npc:${def.id}`,
                kind: 'npc',
                pos: tileToWorld(def.tile.x, def.tile.y),
                radius: NPC_RADIUS,
                ref: def
            });
        }
        for (const def of area.interactables ?? []) {
            this.registry.add({
                id: `int:${def.id}`,
                kind: 'interactable',
                pos: tileToWorld(def.tile.x, def.tile.y),
                ref: def
            });
        }
        // Пропы карты: footprint-объекты, занимают все свои тайлы.
        for (const prop of map.props) {
            this.registry.add({
                id: `prop:${prop.x},${prop.y}`,
                kind: 'prop',
                pos: { x: prop.x + prop.w / 2, y: prop.y + prop.h / 2 },
                footprint: { w: prop.w, h: prop.h },
                ref: prop
            });
        }
    }

    /** NPC на тайле (клик-роутинг). */
    npcAt(x: number, y: number): NpcDef | null {
        return (this.registry.at(x, y, 'npc')?.ref as NpcDef | undefined) ?? null;
    }

    /** Все NPC сцены (кандидаты кнопки действия): статичные + размещения по флагам. */
    npcs(): NpcDef[] {
        const out: NpcDef[] = [];
        for (const obj of this.registry.byKind('npc')) {
            const def = obj.ref as NpcDef | undefined;
            if (def) out.push(def);
        }
        return out;
    }

    /**
     * Дополнительный NPC после создания сцены — размещения по флагам (акт 2):
     * попадает и в клик-роутинг, и в кэш занятых тайлов (если тот уже построен).
     */
    addNpc(def: NpcDef): void {
        this.registry.add({
            id: `npc:${def.id}`,
            kind: 'npc',
            pos: tileToWorld(def.tile.x, def.tile.y),
            radius: NPC_RADIUS,
            ref: def
        });
        this.npcTiles?.add(def.tile.y * 4096 + def.tile.x);
    }

    /** Интерактивный объект на тайле (клик-роутинг). */
    interactableAt(x: number, y: number): InteractableDef | null {
        return (this.registry.at(x, y, 'interactable')?.ref as InteractableDef | undefined) ?? null;
    }

    /** Ключ тайла (как в реестре; NPC не двигаются — кэш навсегда). */
    private npcTiles: Set<number> | null = null;

    /**
     * Grid для построения путей: тайлы NPC считаются занятыми — A* не ведёт
     * героя сквозь тело, где его остановит расталкивание. Остальная карта —
     * живая (IsometricTileMap учитывает стены и пропы).
     */
    walkGrid(): Grid {
        if (!this.npcTiles) {
            this.npcTiles = new Set<number>();
            for (const npc of this.registry.byKind('npc')) {
                this.npcTiles.add(npc.tile.y * 4096 + npc.tile.x);
            }
        }
        const npcTiles = this.npcTiles;
        return {
            width: this.map.width,
            height: this.map.height,
            isWalkable: (x, y) => this.map.isWalkable(x, y) && !npcTiles.has(y * 4096 + x)
        };
    }
}