/**
 * GdSpikes — заменяем cone-примитивы на выбранный шип из spikeFactories.
 *
 * Размеры подгоняем под габариты оригинального cone (PrimitiveTypes.js
 * defaultScale 1.3×1.6×1.3). Низ шипа = верх пола (y=1).
 *
 * Hitbox/коллизия не трогается — она в скрипте уровня по SPIKES[] (по x).
 */
import { Vector3 } from '@babylonjs/core';
import { SPIKE_CATALOG } from '../../admin-preview/gdSpikes/spikeFactories';

// Выбор юзера через /admin-preview/gd-spikes. Эпоха → spike-id.
// Меняется когда юзер сохраняет новый выбор в gd_spike_choices.
const DEFAULT_SPIKE_BY_EPOCH = {
    1: 'e1_v5',   // Терновник
    2: 'e2_v1',   // Кристалл синий
    3: 'e3_v4',
    4: 'e4_v4',
    5: 'e5_v4',
    6: 'e6_v6',
    7: 'e7_v8',
    8: 'e8_v7',
    9: 'e9_v7',
    10: 'e10_v7',
};

export class GdSpikes {
    constructor() {
        this.scene = null;
        this._scene3d = null;
        this._spikeId = null;
        this._spikeRoots = [];
        this._spikeHandles = [];
        this._hiddenOriginals = [];
        this._onBeforeRender = null;
        this._t = 0;
    }

    attach(scene, scene3d, epoch = 1, spikeId = null) {
        if (!scene) return;
        this.scene = scene;
        this._scene3d = scene3d;
        this._spikeId = spikeId || DEFAULT_SPIKE_BY_EPOCH[epoch] || 'e1_v5';
        this._replaceCones();
        this._setupSpin();
    }

    _replaceCones() {
        const pm = this._scene3d?.primitiveManager;
        if (!pm) return;
        const factory = SPIKE_CATALOG.find(s => s.id === this._spikeId);
        if (!factory) {
            console.warn('[GdSpikes] фабрика не найдена:', this._spikeId);
            return;
        }
        // Подгонка: шип должен быть как cone, но реально hitbox = радиус 0.7
        // вокруг данной точки. Визуальный диаметр должен быть ~0.8м, высота ~1.4м.
        const TARGET_H = 1.4;
        const TARGET_W = 0.8;
        // Наши фабрики строят шип высотой ~1.4, диаметром ~0.9
        const FACTORY_H = 1.4;
        const FACTORY_W = 0.9;
        const scaleY = TARGET_H / FACTORY_H;
        const scaleXZ = TARGET_W / FACTORY_W;
        const FLOOR_TOP = 1.0;
        let n = 0;
        for (const data of pm.instances.values()) {
            if (String(data.type) !== 'cone') continue;
            if (typeof data.x !== 'number') continue;
            const handle = factory.make(this.scene, `gd_spike_inst_${data.id}`);
            if (!handle || !handle.root) continue;
            const root = handle.root;
            // Если шип на полу (y близко к 0.8 = центр стандартного cone у пола) —
            // ставим на FLOOR_TOP=1. Иначе сохраняем оригинальную y (потолочные,
            // на платформах, в воздухе).
            const origY = typeof data.y === 'number' ? data.y : FLOOR_TOP;
            const isFloorSpike = Math.abs(origY - 0.8) < 0.3;
            const targetY = isFloorSpike ? FLOOR_TOP : origY;
            root.position = new Vector3(data.x, targetY, data.z || 0);
            root.scaling = new Vector3(scaleXZ, scaleY, scaleXZ);
            // Если оригинал был перевёрнут (rotationX≈π) — переворачиваем и копию.
            const origRotX = data.rotationX || 0;
            if (Math.abs(origRotX - Math.PI) < 0.1) {
                root.rotation.x = Math.PI;
            }
            if (data.mesh) {
                data.mesh.setEnabled(false);
                this._hiddenOriginals.push(data.mesh);
            }
            this._spikeRoots.push(root);
            this._spikeHandles.push(handle);
            n++;
        }
        console.log(`[GdSpikes] заменено ${n} конусов на '${this._spikeId}', scale=(${scaleXZ.toFixed(2)}, ${scaleY.toFixed(2)})`);
    }

    _setupSpin() {
        this._onBeforeRender = () => {
            this._t += 0.01;
            for (let i = 0; i < this._spikeRoots.length; i++) {
                const root = this._spikeRoots[i];
                if (!root) continue;
                // Лёгкое смещение фазы между шипами — чтобы не все одновременно
                root.rotation.y = this._t * 1.3 + i * 0.5;
            }
        };
        this.scene.onBeforeRenderObservable.add(this._onBeforeRender);
    }

    dispose() {
        if (!this.scene) return;
        try {
            if (this._onBeforeRender) {
                this.scene.onBeforeRenderObservable.removeCallback(this._onBeforeRender);
                this._onBeforeRender = null;
            }
        } catch (e) {}
        for (const m of this._hiddenOriginals) {
            try { m.setEnabled(true); } catch (e) {}
        }
        // dispose handles — фабрика умеет освобождать всё, что создала
        for (const h of this._spikeHandles) {
            try { h.dispose && h.dispose(); } catch (e) {}
        }
        this._spikeHandles = [];
        this._spikeRoots = [];
        this._hiddenOriginals = [];
        this._scene3d = null;
        this.scene = null;
    }
}
