/**
 * Сцена — управление 3D-объектами (блоки, примитивы, модели).
 *
 * Каждый объект имеет строковый ref, возвращаемый из spawn / findOne.
 * По ref можно: двигать, красить, удалять, проверять позицию.
 *
 * ВНИМАНИЕ: в Monaco TypeScript объявление функции `delete` невозможно
 * (reserved word). Поэтому метод обозначен через интерфейс SceneApi
 * в declaration ниже, а в реальном game.scene.delete() он работает как
 * обычное свойство объекта.
 */
declare namespace game.scene {
    // ── Создание и удаление ────────────────────────────────────────────

    /**
     * Создать объект на сцене.
     * @param type 'block:<id>' | 'primitive:<id>' | 'model:<id>'
     *   Примеры: 'block:grass', 'primitive:cube', 'primitive:sphere', 'model:tree-pine'.
     * @returns ref для последующих операций или null если тип невалиден.
     * @example
     * const cube = game.scene.spawn('primitive:cube', {
     *     x: 0, y: 5, z: 0,
     *     sx: 2, sy: 2, sz: 2,
     *     color: '#ff0000',
     *     material: 'metal',
     *     name: 'MyCube',
     *     lifetime: 5,  // авто-удаление через 5 сек
     * });
     */
    function spawn(type: string, opts?: {
        x?: number; y?: number; z?: number;
        sx?: number; sy?: number; sz?: number;
        rotationX?: number; rotationY?: number; rotationZ?: number;
        color?: game.Color;
        material?: game.Material;
        name?: string;
        opacity?: number;
        lifetime?: number;
    }): game.Ref | null;

    // Удалить объект по ref. Поскольку 'delete' зарезервированное слово,
    // declaration через дополнительный namespace в самом низу:
    // см. blockEnd ниже — там Object.defineProperty-эмуляция через интерфейс.

    /** Удалить объект через `seconds` секунд (отложенное удаление). */
    function deleteAfter(ref: game.Ref, seconds: number): void;

    /**
     * Создать клон существующего объекта.
     * @param ref исходный объект
     * @param offset смещение от исходной позиции
     * @returns ref нового объекта
     */
    function clone(ref: game.Ref, offset?: Partial<game.Vector3>): game.Ref | null;

    // ── Поиск ──────────────────────────────────────────────────────────

    /** Найти все объекты с указанным именем (для примитивов/моделей с заданным name). */
    function find(name: string): game.Ref[];

    /** Первый объект с этим именем или null. */
    function findOne(name: string): game.Ref | null;

    /** Список ref'ов всех объектов заданного типа. */
    function all(kind: 'block' | 'model' | 'primitive'): game.Ref[];

    // ── Геттеры ────────────────────────────────────────────────────────

    /** Текущая позиция объекта или null если не существует. */
    function getPosition(ref: game.Ref): game.Vector3 | null;

    /** Прочитать произвольное пользовательское данное, ранее сохранённое через setData. */
    function getData(ref: game.Ref, key: string): any;

    // ── Изменение свойств ──────────────────────────────────────────────

    /** Переместить объект в указанную точку. */
    function move(ref: game.Ref, x: number, y: number, z: number): void;

    /** Изменить только позицию по одной оси (например, только Y). */
    function setPosition(ref: game.Ref, pos: Partial<game.Vector3>): void;

    /**
     * Повернуть объект на указанные углы (радианы).
     * Если параметр не указан — текущий угол не меняется.
     */
    function setRotation(ref: game.Ref, rot: { x?: number; y?: number; z?: number }): void;

    /** Поворот объекта на дельта-углы (относительно текущих). */
    function rotate(ref: game.Ref, dx: number, dy: number, dz: number): void;

    /** Сменить цвет (примитивы). */
    function setColor(ref: game.Ref, color: game.Color): void;

    /** Прозрачность 0..1. 1 = непрозрачно, 0 = невидимо. */
    function setOpacity(ref: game.Ref, value: number): void;

    /** Изменить размер объекта (анимировать через game.tween). */
    function setScale(ref: game.Ref, sx: number, sy: number, sz: number): void;

    /** Изменить материал примитива. */
    function setMaterial(ref: game.Ref, name: game.Material): void;

    /** Видимость объекта. */
    function setVisible(ref: game.Ref, visible: boolean): void;

    /** Коллизии объекта. false = игрок проходит сквозь. */
    function setCollide(ref: game.Ref, collide: boolean): void;

    /**
     * Сохранить произвольное данное на объекте.
     * @example
     * game.scene.setData(door, 'locked', true);
     * if (game.scene.getData(door, 'locked')) { /* ... *\/ }
     */
    function setData(ref: game.Ref, key: string, value: any): void;

    /**
     * Установить текстовую метку (Billboard) над объектом.
     *
     * ВНИМАНИЕ: size — **множитель размера плашки в МИРЕ**, НЕ пиксели!
     * 1.0 = плашка ~2.2×0.55 м (это base). 0.6 = ~1.3×0.33 м (рекомендую для ников).
     * Если поставить size: 14 — плашка станет 30 МЕТРОВ и закроет экран.
     *
     * @param ref ref объекта-носителя.
     * @param text текст метки (UTF-8 ок).
     * @param opts.color HEX-цвет шрифта (по умолчанию '#fff').
     * @param opts.size множитель плашки 0.3..1.5 (по умолчанию 1.0).
     * @param opts.height высота над объектом в метрах (по умолчанию 2.5).
     * @example
     * game.scene.setLabel(npc, 'Стив', { color: '#fff', size: 0.6, height: 2 });
     */
    function setLabel(ref: game.Ref, text: string, opts?: {
        color?: game.Color;
        size?: number;
        height?: number;
    }): void;

    /** Убрать Billboard-метку. */
    function clearLabel(ref: game.Ref): void;

    /** Применить текстуру на грани примитива (через AssetManager). */
    function setTexture(ref: game.Ref, assetId: string): void;

    // ── Частицы и эмиттеры ─────────────────────────────────────────────

    /**
     * Эффект частиц в точке. Авто-удаляется через duration секунд.
     * @example
     * game.scene.spawnParticles('fire', { x: 0, y: 2, z: 0 }, { duration: 3, count: 2 });
     */
    function spawnParticles(
        type: 'fire' | 'smoke' | 'sparks' | 'magic' | 'explosion' | 'confetti',
        position: game.Vector3,
        options?: { duration?: number; count?: number; color?: game.Color }
    ): void;

    /** Включить/выключить постоянный эмиттер-объект сцены. */
    function setEmitter(ref: game.Ref, enabled: boolean): void;

    // ── Мобы (Zombie) ──────────────────────────────────────────────────

    /** Получить список мобов на сцене. */
    function mobs(filter?: { type?: string; alive?: boolean }): Array<{
        ref: game.Ref;
        type: string;
        position: game.Vector3;
        hp: number;
    }>;

    /** Убить моба по ref. */
    function killMob(ref: game.Ref): void;

    // ── NPC ────────────────────────────────────────────────────────────

    /**
     * Создать нового NPC из модели. Возвращает прокси с методами moveTo/follow/say/damage/remove.
     * @example
     * const trader = game.scene.spawnNpc('character-a', {
     *     x: 0, y: 1, z: 5, name: 'Торговец', hp: 100,
     * });
     * trader.say('Привет, путник!');
     * trader.moveTo(10, 5);
     */
    function spawnNpc(modelType: string, opts?: {
        x?: number; y?: number; z?: number;
        name?: string;
        hp?: number;
    }): game.NpcRef;

    /** Список созданных NPC. */
    function npcs(filter?: { name?: string; alive?: boolean }): game.NpcRef[];

    // ── Высота поверхности террейна ────────────────────────────────────

    /** Y-координата поверхности террейна в точке (x, z). */
    function surfaceY(x: number, z: number): number;
}

// 'delete' — зарезервированное слово, его нельзя объявить как function,
// но можно через interface-расширение объекта-namespace.
declare namespace game {
    interface SceneNamespaceExtras {
        /** Удалить объект по ref. game.scene.delete('ref_123'). */
        delete(ref: game.Ref): void;
    }
}

