import React, { useState, useMemo, useEffect, useRef } from 'react';
import { getBlockType } from './engine/BlockTypes';
import { getModelType } from './engine/ModelTypes';
import { getPrimitiveType } from './engine/PrimitiveTypes';
import HoverPlusMenu from './HoverPlusMenu';
import Icon from './Icon';
import cl from './HierarchyPanel.module.css';

/**
 * Рендер иконки строки иерархии. Принимает:
 *  - JSX  → отрисовывает как есть
 *  - { name } объект → Icon с name
 *  - строку-эмодзи → пробует EMOJI_TO_NAME в Icon
 *  - строку (не эмодзи) → как имя для Icon
 *
 * Возвращает <Icon/> или null. Размер 14px (плотность строки иерархии).
 */
const renderRowIcon = (val) => {
    if (!val) return null;
    if (typeof val === 'string') {
        // Если в строке только ASCII-символы — это семантическое имя.
        // Если эмодзи — обрабатываем через Icon.emoji.
        const isAscii = /^[\x20-\x7E_-]+$/.test(val);
        return isAscii
            ? <Icon name={val} size={14} />
            : <Icon emoji={val} size={14} />;
    }
    // JSX
    return val;
};

/**
 * ItemRow — общая строка иерархии с поддержкой hover-«+»-меню.
 * При наведении мышью справа появляется кнопка «+», по клику показываются
 * пункты `plusItems` (передаются в HoverPlusMenu).
 */
const ItemRow = ({
    icon, label, title, depth = 0, selected, plusItems,
    onClick, onDoubleClick, onContextMenu, onDragStart, draggable,
    extraStyle, selId, badge,
}) => {
    const [hovered, setHovered] = useState(false);
    const rowRef = React.useRef(null);
    // Когда строка стала выделенной — подскроллить её в видимую зону дерева
    // (после авто-раскрытия веток объект может оказаться вне видимой области).
    useEffect(() => {
        if (selected && rowRef.current) {
            rowRef.current.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
        }
    }, [selected]);
    return (
        <div
            ref={rowRef}
            className={`item ${selected ? 'item-selected' : ''}`}
            style={{
                display: 'flex',
                alignItems: 'center',
                gap: 6,
                // Фиксированная высота — чтобы появление кнопки-плюса при hover
                // не «дёргало» высоту строки и весь список снизу не прыгал.
                height: 26,
                padding: '0 8px',
                paddingLeft: depth * 12 + 8,
                cursor: 'pointer',
                background: selected
                    ? 'linear-gradient(90deg, rgba(79, 116, 255, 0.28) 0%, rgba(79, 116, 255, 0.08) 100%)'
                    : (hovered ? '#2e2e2e' : 'transparent'),
                borderLeft: selected ? '2px solid #4f74ff' : '2px solid transparent',
                color: selected ? '#6d8aff' : '#e8e8ea',
                fontWeight: selected ? 700 : 600,
                fontSize: 12.5,
                userSelect: 'none',
                ...extraStyle,
            }}
            draggable={draggable}
            onDragStart={onDragStart}
            onClick={onClick}
            onDoubleClick={onDoubleClick}
            onContextMenu={onContextMenu}
            onMouseEnter={() => setHovered(true)}
            onMouseLeave={() => setHovered(false)}
            title={title || label}
            data-sel-id={selId}
        >
            <span style={{ width: 18, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>{renderRowIcon(icon)}</span>
            <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0 }}>{label}</span>
            {badge && (
                <span style={{ flexShrink: 0, display: 'inline-flex' }}>{badge}</span>
            )}
            {plusItems && plusItems.length > 0 && (
                <HoverPlusMenu visible={hovered} items={plusItems} />
            )}
        </div>
    );
};

/** Строка-заголовок категории (Сцена/Освещение/Звук/Игрок/Скрипты). */
const GroupRow = ({ icon, label, open, onToggle, plusItems }) => {
    const [hovered, setHovered] = useState(false);
    return (
        <div
            onClick={onToggle}
            onMouseEnter={() => setHovered(true)}
            onMouseLeave={() => setHovered(false)}
            style={{
                display: 'flex',
                alignItems: 'center',
                gap: 6,
                padding: '6px 10px',
                cursor: 'pointer',
                color: '#e8e8ea',
                fontWeight: 700,
                fontSize: 12.5,
                letterSpacing: 0.1,
                userSelect: 'none',
                background: hovered ? '#2e2e2e' : 'transparent',
                borderTop: '1px solid #303030',
            }}
        >
            <span style={{ width: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: 0.8, flexShrink: 0 }}>
                <Icon name={open ? 'chevronDown' : 'chevronRight'} size={10} strokeWidth={2.5} />
            </span>
            {icon && <span style={{ display: 'flex', alignItems: 'center', flexShrink: 0 }}>{renderRowIcon(icon)}</span>}
            <span style={{ flex: 1 }}>{label}</span>
            {plusItems && plusItems.length > 0 && (
                <HoverPlusMenu visible={hovered} items={plusItems} />
            )}
        </div>
    );
};

/** Строка скрипта внутри иерархии. */
const ScriptRow = ({ script, depth, selected, onSelect, onDelete, onRename, onContextMenu, onStartRename }) => {
    const displayName = script.name || (script.id === 'demo' ? 'Демо-скрипт' : script.id);
    // Lua — либо явно language='lua', либо импортированный .rbxl-скрипт
    // (хранится с language='js' в БД но фактически Lua-код внутри обёртки).
    const isRbxlImported = typeof script.code === 'string'
        && script.code.startsWith('// @roblox-lua');
    const isLua = script.language === 'lua' || isRbxlImported;
    const badge = (
        <span
            style={{
                fontSize: 9,
                fontWeight: 800,
                padding: '1px 5px',
                borderRadius: 4,
                lineHeight: 1.4,
                letterSpacing: 0.4,
                marginRight: 4,
                background: isLua ? '#2196f3' : '#f7df1e',
                color: isLua ? '#fff' : '#1a1a1c',
            }}
            title={isLua ? 'Lua (Roblox API)' : 'JavaScript (game.* API)'}
        >
            {isLua ? 'LUA' : 'JS'}
        </span>
    );
    return (
        <ItemRow
            icon="📜"
            label={displayName}
            title={`${displayName} (id: ${script.id}, язык: ${isLua ? 'Lua' : 'JavaScript'})`}
            depth={depth}
            selected={selected}
            onClick={onSelect}
            onContextMenu={onContextMenu}
            badge={badge}
            plusItems={[
                {
                    id: 'rename', label: 'Переименовать', icon: '✏️',
                    onClick: () => onStartRename?.(displayName),
                },
                { divider: true },
                { id: 'delete', label: 'Удалить', icon: '🗑', danger: true, onClick: onDelete },
            ]}
            extraStyle={{ color: '#3357ff' }}
        />
    );
};

/** Inline-строка переименования (input в иерархии). Enter — commit, Esc — cancel. */
const RenameRow = ({ depth = 0, icon, value, onChange, onCommit, onCancel }) => {
    const ref = React.useRef(null);
    React.useEffect(() => {
        const el = ref.current;
        if (el) { el.focus(); el.select(); }
    }, []);
    return (
        <div
            style={{
                display: 'flex',
                alignItems: 'center',
                gap: 6,
                padding: '4px 8px',
                paddingLeft: depth * 12 + 8,
                background: 'rgba(51, 87, 255, 0.10)',
                borderLeft: '2px solid #3357ff',
                userSelect: 'none',
            }}
            onClick={(e) => e.stopPropagation()}
        >
            <span style={{ lineHeight: 1, width: 18, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{renderRowIcon(icon)}</span>
            <input
                ref={ref}
                value={value}
                onChange={(e) => onChange?.(e.target.value)}
                onBlur={onCommit}
                onKeyDown={(e) => {
                    e.stopPropagation();
                    if (e.key === 'Enter') { e.preventDefault(); onCommit?.(); }
                    else if (e.key === 'Escape') { e.preventDefault(); onCancel?.(); }
                }}
                style={{
                    flex: 1,
                    minWidth: 0,
                    padding: '4px 8px',
                    background: '#1b1b1b',
                    border: '1.5px solid #4f74ff',
                    borderRadius: 6,
                    color: '#e8e8ea',
                    fontSize: 12,
                    fontWeight: 600,
                    outline: 'none',
                    boxShadow: '0 0 0 3px rgba(79, 116, 255, 0.22)',
                    fontFamily: '"Roboto Condensed", system-ui, sans-serif',
                }}
            />
        </div>
    );
};

/** Сравнение target-ссылки скрипта с объектом-носителем. */
function targetMatches(target, kind, ref) {
    if (!target || target.kind !== kind) return false;
    if (kind === 'block') {
        // Поддерживаем оба формата: { x, y, z } и { ref: { x, y, z } }
        const tr = target.ref || target;
        return tr.x === ref.x && tr.y === ref.y && tr.z === ref.z;
    }
    const tid = target.id ?? target.ref;
    return tid === ref;
}

/**
 * Hierarchy — дерево объектов сцены с поддержкой папок.
 *
 * Объект (блок/модель/примитив) живёт либо в корне (folderId=null),
 * либо в папке (folderId=N). Папка тоже может быть вложена в другую папку.
 *
 * Props:
 *   blocks         — массив [{gridX, gridY, gridZ, blockTypeId, folderId}]
 *   models         — массив [{instanceId, modelTypeId, x, y, z, folderId}]
 *   primitives     — массив [{id, type, x, y, z, ..., folderId}]
 *   folders        — массив [{id, name, parentId, visible}]
 *   selection      — текущее выделение
 *   onSelectBlock(x,y,z) / onSelectModel(id) / onSelectPrimitive(id) / onSelectSpawn()
 *   onDeleteBlock / onDeleteModel / onDeletePrimitive
 *   onFocusSelection()
 *   onCreateFolder(name, parentId)
 *   onRenameFolder(id, name)
 *   onRemoveFolder(id, deleteContent)
 *   onSetFolderVisible(id, visible)
 *   onAssignToFolder(kind, ref, folderId)   — переместить объект/папку в папку
 */
const HierarchyPanel = ({
    blocks, models, primitives = [], userModels = [], folders = [],
    selection,
    onSelectUserModel, onDeleteUserModel, onRenameUserModel,
    onSelectBlock, onSelectModel, onSelectPrimitive, onSelectSpawn, onSelectLighting,
    onSelectSound, onSelectPlayer, onSelectPlayerProps, onSelectFloor,
    guiElements = [], onSelectGui, onCreateGui, onDeleteGui, onRenameGui, onMoveGuiZ, onSetGuiParent,
    guiOverlayHidden = false, onToggleGuiOverlay,
    floorEnabled = true, onCreateFloor, onDeleteFloor, onDeleteSpawn, spawnEnabled = true,
    onSelectFolder,
    scripts = [], onSelectScript, onCreateScript, onDeleteScript,
    onRenameModel, onRenamePrimitive, onRenameScript,
    /**
     * onCreateScript(target) — создать новый скрипт.
     * target: null (глобальный) | { kind: 'block', x, y, z } | { kind: 'model', id }
     *       | { kind: 'primitive', id } | { kind: 'folder', id }
     */
    onDeleteBlock, onDeleteModel, onDeletePrimitive,
    onFocusSelection,
    onCreateFolder, onRenameFolder, onRemoveFolder,
    onSetFolderVisible, onAssignToFolder,
    // Фаза 5.5 — библиотека пользовательских звуков.
    soundList = [], onUploadSound, onRemoveSound,
}) => {
    // input[file] для загрузки звука (Фаза 5.5).
    const soundFileRef = React.useRef(null);
    const [openFolders, setOpenFolders] = useState(() => new Set());
    const [rootBlocksOpen, setRootBlocksOpen] = useState(false);
    const [rootPrimsOpen, setRootPrimsOpen] = useState(false);
    const [rootModelsOpen, setRootModelsOpen] = useState(false);
    const [rootUserModelsOpen, setRootUserModelsOpen] = useState(false);
    // Главные «service»-категории (Roblox-style). По умолчанию свёрнуты.
    const [workspaceOpen, setWorkspaceOpen] = useState(false);
    const [lightingOpen, setLightingOpen] = useState(false);
    const [soundOpen, setSoundOpen] = useState(false);
    const [playerOpen, setPlayerOpen] = useState(false);
    const [scriptsOpen, setScriptsOpen] = useState(false);
    const [guiOpen, setGuiOpen] = useState(false);
    const [contextMenu, setContextMenu] = useState(null);
    const [renameFolderId, setRenameFolderId] = useState(null);
    const [renameValue, setRenameValue] = useState('');
    // Универсальный inline-rename для остальных типов: модель/примитив/скрипт.
    // { kind: 'model'|'primitive'|'script', refKey: string, value: string } | null
    const [renaming, setRenaming] = useState(null);

    // Авто-скролл к выбранному элементу: когда юзер выделяет объект в 3D-сцене
    // (или приходит выделение извне) — раскрываем родительские папки и
    // прокручиваем иерархию к нему.
    const hierarchyRootRef = useRef(null);
    useEffect(() => {
        if (!selection) return;
        let selId = null;
        if (selection.type === 'primitive') selId = `primitive:${selection.id}`;
        else if (selection.type === 'model') selId = `model:${selection.instanceId}`;
        else if (selection.type === 'block') selId = `block:${selection.gridX},${selection.gridY},${selection.gridZ}`;
        if (!selId) return;

        // 1) Раскрываем ВСЁ что может скрывать выбранный объект:
        //    - все папки (folders) - вдруг примитив сидит в Folder
        //    - rootPrimsOpen - группа "Примитивы" в корне
        //    - sceneOpen — корень сцены
        if (folders && folders.length > 0) {
            setOpenFolders(prev => {
                const next = new Set(prev);
                let changed = false;
                for (const f of folders) {
                    if (!next.has(f.id)) { next.add(f.id); changed = true; }
                }
                return changed ? next : prev;
            });
        }
        if (!workspaceOpen) setWorkspaceOpen(true);
        if (selection.type === 'primitive' && !rootPrimsOpen) setRootPrimsOpen(true);
        if (selection.type === 'block' && !rootBlocksOpen) setRootBlocksOpen(true);
        if (selection.type === 'model' && !rootModelsOpen) setRootModelsOpen(true);

        // 2) Скролл через 2 кадра (даём React перерендерить после раскрытия)
        const tick = () => {
            const root = hierarchyRootRef.current;
            if (!root) return;
            const el = root.querySelector(`[data-sel-id="${CSS.escape(selId)}"]`);
            if (el && typeof el.scrollIntoView === 'function') {
                el.scrollIntoView({ block: 'center', behavior: 'smooth' });
            }
        };
        const raf1 = requestAnimationFrame(() => {
            const raf2 = requestAnimationFrame(tick);
            return () => cancelAnimationFrame(raf2);
        });
        return () => cancelAnimationFrame(raf1);
    }, [selection?.type, selection?.id, selection?.instanceId, selection?.gridX, selection?.gridY, selection?.gridZ, folders?.length, workspaceOpen, rootPrimsOpen, rootBlocksOpen, rootModelsOpen, openFolders]);

    const startRename = (kind, refKey, currentValue) => {
        setRenaming({ kind, refKey, value: currentValue || '' });
    };
    const commitRename = () => {
        if (!renaming) return;
        const v = (renaming.value || '').trim();
        const { kind, refKey } = renaming;
        setRenaming(null);
        if (!v) return; // пусто — отмена
        if (kind === 'model') {
            onRenameModel?.(Number(refKey), v);
        } else if (kind === 'primitive') {
            onRenamePrimitive?.(Number(refKey), v);
        } else if (kind === 'script') {
            onRenameScript?.(refKey, v);
        }
    };
    const cancelRename = () => setRenaming(null);

    // Группируем папки по parentId
    const foldersByParent = useMemo(() => {
        const map = new Map();
        for (const f of folders) {
            const k = f.parentId ?? null;
            if (!map.has(k)) map.set(k, []);
            map.get(k).push(f);
        }
        return map;
    }, [folders]);

    // Группируем объекты по folderId
    const blocksByFolder = useMemo(() => {
        const map = new Map();
        for (const b of blocks) {
            const k = b.folderId ?? null;
            if (!map.has(k)) map.set(k, []);
            map.get(k).push(b);
        }
        return map;
    }, [blocks]);

    const modelsByFolder = useMemo(() => {
        const map = new Map();
        for (const m of models) {
            const k = m.folderId ?? null;
            if (!map.has(k)) map.set(k, []);
            map.get(k).push(m);
        }
        return map;
    }, [models]);

    const primitivesByFolder = useMemo(() => {
        const map = new Map();
        for (const p of primitives) {
            const k = p.folderId ?? null;
            if (!map.has(k)) map.set(k, []);
            map.get(k).push(p);
        }
        return map;
    }, [primitives]);

    const userModelsByFolder = useMemo(() => {
        const map = new Map();
        for (const um of userModels) {
            const k = um.folderId ?? null;
            if (!map.has(k)) map.set(k, []);
            map.get(k).push(um);
        }
        return map;
    }, [userModels]);

    const isBlockSelected = (b) =>
        selection?.type === 'block' &&
        selection.gridX === b.gridX && selection.gridY === b.gridY && selection.gridZ === b.gridZ;
    const isModelSelected = (m) =>
        selection?.type === 'model' && selection.instanceId === m.instanceId;
    const isUserModelSelected = (um) =>
        selection?.type === 'userModel' && selection.instanceId === um.instanceId;
    const isPrimitiveSelected = (p) =>
        selection?.type === 'primitive' && selection.id === p.id;

    const toggleFolder = (id) => {
        setOpenFolders(prev => {
            const n = new Set(prev);
            if (n.has(id)) n.delete(id); else n.add(id);
            return n;
        });
    };

    // === Авто-раскрытие пути до выделенного объекта ===
    // Когда объект выбирают мышкой на сцене (или из скрипта), он должен стать
    // ВИДИМЫМ в дереве: раскрываем «Сцену», нужную под-группу (Блоки/Примитивы/
    // Модели) и всю цепочку папок-родителей. Подсветка строки уже работает через
    // проп `selection`; здесь только разворачиваем свёрнутые ветки.
    useEffect(() => {
        if (!selection) return;
        const t = selection.type;
        // Выделена ПАПКА (клик по сцене / вставка кита из тулбокса) — раскрыть
        // «Сцену» и цепочку папок до неё, чтобы папка стала видна в дереве.
        if (t === 'folder') {
            setWorkspaceOpen(true);
            setOpenFolders(prev => {
                const n = new Set(prev);
                let cur = selection.folderId;
                const guard = new Set();
                while (cur != null && !guard.has(cur)) {
                    guard.add(cur);
                    const f = folders.find(ff => ff.id === cur);
                    if (f && f.parentId != null) { n.add(f.parentId); cur = f.parentId; } else cur = null;
                }
                return n;
            });
            return;
        }
        // Находим объект и его folderId по выделению.
        let obj = null, kind = null;
        if (t === 'block') {
            obj = blocks.find(b => b.gridX === selection.gridX && b.gridY === selection.gridY && b.gridZ === selection.gridZ);
            kind = 'block';
        } else if (t === 'primitive') {
            obj = primitives.find(p => p.id === selection.id);
            kind = 'primitive';
        } else if (t === 'model') {
            obj = models.find(m => m.instanceId === selection.instanceId);
            kind = 'model';
        } else if (t === 'userModel') {
            obj = userModels.find(um => um.instanceId === selection.instanceId);
            kind = 'userModel';
        } else if (t === 'spawn' || t === 'floor') {
            kind = 'workspace-only';
        }
        if (!kind) return; // lighting/sound/player/gui/script — свои группы, не трогаем

        // 1) Раскрыть корневую категорию «Сцена».
        setWorkspaceOpen(true);

        if (kind === 'workspace-only') return;

        const folderId = obj?.folderId ?? null;
        if (folderId == null) {
            // 2a) Объект в корне — раскрыть его под-группу (Блоки/Примитивы/Модели).
            if (kind === 'block') setRootBlocksOpen(true);
            else if (kind === 'primitive') setRootPrimsOpen(true);
            else if (kind === 'model') setRootModelsOpen(true);
            else if (kind === 'userModel') setRootUserModelsOpen(true);
        } else {
            // 2b) Объект в папке — раскрыть всю цепочку папок-родителей.
            setOpenFolders(prev => {
                const n = new Set(prev);
                let cur = folderId;
                const guard = new Set(); // защита от циклов
                while (cur != null && !guard.has(cur)) {
                    guard.add(cur);
                    n.add(cur);
                    const f = folders.find(ff => ff.id === cur);
                    cur = f ? (f.parentId ?? null) : null;
                }
                return n;
            });
        }
    // Триггер — только смена выделения (не трогаем при добавлении объектов,
    // чтобы не переоткрывать ветки, которые пользователь свернул вручную).
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [selection]);

    const handleContextMenu = (e, item) => {
        e.preventDefault();
        e.stopPropagation();
        setContextMenu({ x: e.clientX, y: e.clientY, item });
    };

    const closeContext = () => setContextMenu(null);

    const startRenameFolder = (folder) => {
        setRenameFolderId(folder.id);
        setRenameValue(folder.name);
        closeContext();
    };

    const commitRenameFolder = () => {
        if (renameFolderId != null && renameValue.trim()) {
            onRenameFolder?.(renameFolderId, renameValue.trim());
        }
        setRenameFolderId(null);
        setRenameValue('');
    };

    // Drag&drop объектов в папки
    const handleDragStart = (e, payload) => {
        e.dataTransfer.setData('application/json', JSON.stringify(payload));
        e.dataTransfer.effectAllowed = 'move';
    };
    const handleDragOver = (e) => {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
    };
    const handleDropOnFolder = (e, folderId) => {
        e.preventDefault();
        e.stopPropagation();
        try {
            const data = JSON.parse(e.dataTransfer.getData('application/json'));
            if (data.kind === 'block') {
                onAssignToFolder?.('block', { x: data.x, y: data.y, z: data.z }, folderId);
            } else if (data.kind === 'model') {
                onAssignToFolder?.('model', data.id, folderId);
            } else if (data.kind === 'primitive') {
                onAssignToFolder?.('primitive', data.id, folderId);
            }
        } catch (err) { /* ignore */ }
    };
    const handleDropOnRoot = (e) => {
        handleDropOnFolder(e, null);
    };

    /** Рекурсивный рендер папки и её содержимого. */
    const renderFolder = (folder, depth) => {
        const isOpen = openFolders.has(folder.id);
        const subFolders = foldersByParent.get(folder.id) || [];
        const subBlocks = blocksByFolder.get(folder.id) || [];
        const subModels = modelsByFolder.get(folder.id) || [];
        const subPrims = primitivesByFolder.get(folder.id) || [];
        const subUserModels = userModelsByFolder.get(folder.id) || [];
        const totalCount = subBlocks.length + subModels.length + subPrims.length + subUserModels.length + subFolders.length;

        const folderSelected = selection?.type === 'folder' && selection?.folderId === folder.id;
        return (
            <div key={`folder-${folder.id}`} className={cl.folderWrap}>
                <div
                    ref={folderSelected ? (el) => { if (el) el.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } : null}
                    className={`${cl.folderHeader} ${folderSelected ? cl.itemSelected : ''}`}
                    style={{ paddingLeft: depth * 12 + 8 }}
                    onClick={() => onSelectFolder?.(folder.id)}
                    onContextMenu={(e) => handleContextMenu(e, { type: 'folder', ...folder })}
                    onDragOver={handleDragOver}
                    onDrop={(e) => handleDropOnFolder(e, folder.id)}
                >
                    <span className={cl.chevron} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
                        onClick={(e) => { e.stopPropagation(); toggleFolder(folder.id); }}
                    >
                        <Icon name={isOpen ? 'chevronDown' : 'chevronRight'} size={10} strokeWidth={2.5} />
                    </span>
                    <span className={cl.folderIcon} style={{ display: 'flex', alignItems: 'center' }}>
                        <Icon name={isOpen ? 'folder-open' : 'folder'} size={14} />
                    </span>
                    {renameFolderId === folder.id ? (
                        <input
                            className={cl.renameInput}
                            value={renameValue}
                            autoFocus
                            onChange={(e) => setRenameValue(e.target.value)}
                            onClick={(e) => e.stopPropagation()}
                            onKeyDown={(e) => {
                                if (e.key === 'Enter') commitRenameFolder();
                                if (e.key === 'Escape') { setRenameFolderId(null); setRenameValue(''); }
                            }}
                            onBlur={commitRenameFolder}
                        />
                    ) : (
                        <span className={cl.folderName}>{folder.name} <span className={cl.folderCount}>({totalCount})</span></span>
                    )}
                    <button
                        className={cl.eyeBtn}
                        title={folder.visible ? 'Скрыть папку' : 'Показать папку'}
                        onClick={(e) => {
                            e.stopPropagation();
                            onSetFolderVisible?.(folder.id, !folder.visible);
                        }}
                    >
                        <Icon name={folder.visible ? 'visible' : 'hidden'} size={14} />

                    </button>
                </div>
                {isOpen && (
                    <>
                        {subFolders.map(sf => renderFolder(sf, depth + 1))}
                        {subBlocks.map(b => renderBlockItem(b, depth + 1))}
                        {subPrims.map(p => renderPrimitiveItem(p, depth + 1))}
                        {subModels.map(m => renderModelItem(m, depth + 1))}
                        {subUserModels.map(um => renderUserModelItem(um, depth + 1))}
                        {totalCount === 0 && (
                            <div className={cl.empty} style={{ paddingLeft: (depth + 1) * 12 + 8 }}>пусто</div>
                        )}
                    </>
                )}
            </div>
        );
    };

    /** Рендер строки скрипта с поддержкой inline-rename и ПКМ-меню. */
    const renderScript = (s, depth) => {
        const displayName = s.name || (s.id === 'demo' ? 'Демо-скрипт' : s.id);
        if (renaming?.kind === 'script' && renaming.refKey === s.id) {
            return (
                <RenameRow
                    key={`s-rename-${s.id}`}
                    depth={depth}
                    icon="📜"
                    value={renaming.value}
                    onChange={(v) => setRenaming(prev => prev ? { ...prev, value: v } : prev)}
                    onCommit={commitRename}
                    onCancel={cancelRename}
                />
            );
        }
        return (
            <ScriptRow
                key={`s-${s.id}`}
                script={s}
                depth={depth}
                selected={selection?.type === 'script' && selection?.scriptId === s.id}
                onSelect={() => onSelectScript?.(s.id)}
                onDelete={() => onDeleteScript?.(s.id)}
                onStartRename={(currentName) => startRename('script', s.id, currentName)}
                onContextMenu={(e) => handleContextMenu(e, { type: 'script', id: s.id, name: displayName })}
            />
        );
    };

    // === Скрипты-потомки одного объекта ===
    const renderNestedScriptsFor = (kind, ref, depth) => {
        const nested = scripts.filter(s => targetMatches(s.target, kind, ref));
        if (nested.length === 0) return null;
        return nested.map(s => renderScript(s, depth + 1));
    };

    const renderBlockItem = (b, depth) => {
        const type = getBlockType(b.blockTypeId);
        const targetRef = { x: b.gridX, y: b.gridY, z: b.gridZ };
        return (
            <React.Fragment key={`b-${b.gridX},${b.gridY},${b.gridZ}`}>
                <ItemRow
                    icon="🧱"
                    label={`${type?.name || b.blockTypeId} (${b.gridX},${b.gridY},${b.gridZ})`}
                    title={`${type?.name} (${b.gridX}, ${b.gridY}, ${b.gridZ})`}
                    depth={depth}
                    selected={isBlockSelected(b)}
                    selId={`block:${b.gridX},${b.gridY},${b.gridZ}`}
                    draggable
                    onDragStart={(e) => handleDragStart(e, { kind: 'block', x: b.gridX, y: b.gridY, z: b.gridZ })}
                    onClick={() => onSelectBlock(b.gridX, b.gridY, b.gridZ)}
                    onDoubleClick={() => { onSelectBlock(b.gridX, b.gridY, b.gridZ); onFocusSelection?.(); }}
                    onContextMenu={(e) => handleContextMenu(e, { type: 'block', ...b })}
                    plusItems={[
                        {
                            id: 'add-script', label: 'Скрипт', icon: '📜',
                            onClick: () => onCreateScript?.({ kind: 'block', x: b.gridX, y: b.gridY, z: b.gridZ }),
                        },
                        { divider: true },
                        {
                            id: 'delete', label: 'Удалить', icon: '🗑', danger: true,
                            onClick: () => onDeleteBlock?.(b.gridX, b.gridY, b.gridZ),
                        },
                    ]}
                />
                {renderNestedScriptsFor('block', targetRef, depth)}
            </React.Fragment>
        );
    };

    const renderModelItem = (m, depth) => {
        const type = getModelType(m.modelTypeId);
        const displayName = m.name || type?.name || m.modelTypeId;
        const isRenaming = renaming?.kind === 'model' && Number(renaming.refKey) === m.instanceId;
        return (
            <React.Fragment key={`m-${m.instanceId}`}>
                {isRenaming ? (
                    <RenameRow
                        depth={depth}
                        icon="🌳"
                        value={renaming.value}
                        onChange={(v) => setRenaming(prev => prev ? { ...prev, value: v } : prev)}
                        onCommit={commitRename}
                        onCancel={cancelRename}
                    />
                ) : (
                    <ItemRow
                        icon="🌳"
                        label={displayName}
                        title={`${displayName} (${m.x.toFixed(1)}, ${m.y.toFixed(1)}, ${m.z.toFixed(1)})`}
                        depth={depth}
                        selected={isModelSelected(m)}
                        selId={`model:${m.instanceId}`}
                        draggable
                        onDragStart={(e) => handleDragStart(e, { kind: 'model', id: m.instanceId })}
                        onClick={() => onSelectModel(m.instanceId)}
                        onDoubleClick={() => { onSelectModel(m.instanceId); onFocusSelection?.(); }}
                        onContextMenu={(e) => handleContextMenu(e, { type: 'model', ...m })}
                        plusItems={[
                            {
                                id: 'add-script', label: 'Скрипт', icon: '📜',
                                onClick: () => onCreateScript?.({ kind: 'model', id: m.instanceId }),
                            },
                            {
                                id: 'rename', label: 'Переименовать', icon: '✏️',
                                onClick: () => startRename('model', String(m.instanceId), displayName),
                            },
                            { divider: true },
                            {
                                id: 'delete', label: 'Удалить', icon: '🗑', danger: true,
                                onClick: () => onDeleteModel?.(m.instanceId),
                            },
                        ]}
                    />
                )}
                {renderNestedScriptsFor('model', m.instanceId, depth)}
            </React.Fragment>
        );
    };

    const renderUserModelItem = (um, depth) => {
        const displayName = um.name || 'Моя модель';
        return (
            <React.Fragment key={`um-${um.instanceId}`}>
                <ItemRow
                    icon="🧊"
                    label={`${displayName} (${um.x.toFixed(1)}, ${um.y.toFixed(1)}, ${um.z.toFixed(1)})`}
                    title={`${displayName} — пользовательская модель (id: ${um.instanceId})`}
                    depth={depth}
                    selected={isUserModelSelected(um)}
                    draggable
                    onDragStart={(e) => handleDragStart(e, { kind: 'userModel', id: um.instanceId })}
                    onClick={() => onSelectUserModel?.(um.instanceId)}
                    onDoubleClick={() => { onSelectUserModel?.(um.instanceId); onFocusSelection?.(); }}
                    onContextMenu={(e) => handleContextMenu(e, { type: 'userModel', ...um })}
                    plusItems={[
                        {
                            id: 'add-script', label: 'Скрипт', icon: '📜',
                            onClick: () => onCreateScript?.({ kind: 'userModel', id: um.instanceId }),
                        },
                        { divider: true },
                        {
                            id: 'delete', label: 'Удалить', icon: '🗑', danger: true,
                            onClick: () => onDeleteUserModel?.(um.instanceId),
                        },
                    ]}
                />
                {renderNestedScriptsFor('userModel', um.instanceId, depth)}
            </React.Fragment>
        );
    };

    const renderPrimitiveItem = (p, depth) => {
        const def = getPrimitiveType(p.type);
        const displayName = p.name || def?.name || p.type;
        const isRenaming = renaming?.kind === 'primitive' && Number(renaming.refKey) === p.id;
        return (
            <React.Fragment key={`p-${p.id}`}>
                {isRenaming ? (
                    <RenameRow
                        depth={depth}
                        icon={def?.icon || '⬛'}
                        value={renaming.value}
                        onChange={(v) => setRenaming(prev => prev ? { ...prev, value: v } : prev)}
                        onCommit={commitRename}
                        onCancel={cancelRename}
                    />
                ) : (
                    <ItemRow
                        icon={def?.icon || '⬛'}
                        label={`${displayName} (${p.x.toFixed(1)}, ${p.y.toFixed(1)}, ${p.z.toFixed(1)})`}
                        title={`${displayName} (${p.x.toFixed(1)}, ${p.y.toFixed(1)}, ${p.z.toFixed(1)})`}
                        depth={depth}
                        selected={isPrimitiveSelected(p)}
                        selId={`primitive:${p.id}`}
                        draggable
                        onDragStart={(e) => handleDragStart(e, { kind: 'primitive', id: p.id })}
                        onClick={() => onSelectPrimitive?.(p.id)}
                        onDoubleClick={() => { onSelectPrimitive?.(p.id); onFocusSelection?.(); }}
                        onContextMenu={(e) => handleContextMenu(e, { type: 'primitive', ...p })}
                        plusItems={[
                            {
                                id: 'add-script', label: 'Скрипт', icon: '📜',
                                onClick: () => onCreateScript?.({ kind: 'primitive', id: p.id }),
                            },
                            {
                                id: 'rename', label: 'Переименовать', icon: '✏️',
                                onClick: () => startRename('primitive', String(p.id), displayName),
                            },
                            { divider: true },
                            {
                                id: 'delete', label: 'Удалить', icon: '🗑', danger: true,
                                onClick: () => onDeletePrimitive?.(p.id),
                            },
                        ]}
                    />
                )}
                {renderNestedScriptsFor('primitive', p.id, depth)}
            </React.Fragment>
        );
    };

    // Объекты в корне
    const rootFolders = foldersByParent.get(null) || [];
    const rootBlocks = blocksByFolder.get(null) || [];
    const rootModels = modelsByFolder.get(null) || [];
    const rootUserModels = userModelsByFolder.get(null) || [];
    const rootPrims = primitivesByFolder.get(null) || [];

    return (
        <div className={cl.hierarchy} onClick={closeContext} ref={hierarchyRootRef}>
            <div className={cl.root}
                 onDragOver={handleDragOver}
                 onDrop={handleDropOnRoot}>
                <div className={cl.rootLine} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <Icon name="globe" size={14} /> <b>Сцена</b>
                </div>

                {/* === 📦 СЦЕНА === (Workspace) — все объекты мира */}
                <GroupRow
                    icon="📦"
                    label="Сцена"
                    open={workspaceOpen}
                    onToggle={() => setWorkspaceOpen(!workspaceOpen)}
                    plusItems={[
                        {
                            id: 'add-folder', label: 'Папка', icon: '📁',
                            onClick: () => onCreateFolder?.('Новая папка', null),
                        },
                        // Если пол удалён — показываем «Добавить пол»
                        ...(!floorEnabled ? [{
                            id: 'add-floor', label: 'Пол', icon: '🟦',
                            onClick: () => onCreateFloor?.(),
                        }] : []),
                    ]}
                />
                {workspaceOpen && (
                    <div style={{ paddingLeft: 8 }}>
                        {/* Точка спавна — кликабельная. Скрыта если удалена. */}
                        {spawnEnabled !== false && (
                        <div
                            className={`${cl.item} ${selection?.type === 'spawn' ? cl.itemSelected : ''}`}
                            onClick={() => onSelectSpawn?.()}
                            onDoubleClick={() => { onSelectSpawn?.(); onFocusSelection?.(); }}
                            onContextMenu={(e) => { onSelectSpawn?.(); handleContextMenu(e, { type: 'spawn' }); }}
                            title="Точка спавна игрока (ПКМ — меню, Delete — удалить)"
                        >
                            <span className={cl.itemIcon} style={{ display: 'inline-flex' }}><Icon name="flag" size={14} /></span>
                            <span className={cl.itemLabel}>Точка спавна</span>
                        </div>
                        )}

                        {/* Пол — псевдо-объект, если включён */}
                        {floorEnabled && (
                            <ItemRow
                                icon="🟦"
                                label="Пол"
                                title="Плоскость с сеткой в редакторе"
                                depth={0}
                                selected={selection?.type === 'floor'}
                                onClick={() => onSelectFloor?.()}
                                onContextMenu={(e) => handleContextMenu(e, { type: 'floor' })}
                                plusItems={[
                                    {
                                        id: 'delete-floor', label: 'Удалить', icon: '🗑', danger: true,
                                        onClick: () => onDeleteFloor?.(),
                                    },
                                ]}
                            />
                        )}

                        {/* Папки в корне (рекурсивно) */}
                        {rootFolders.map(f => renderFolder(f, 0))}

                        {/* Свободные блоки в корне */}
                        {rootBlocks.length > 0 && (
                            <>
                                <div
                                    className={cl.groupHeader}
                                    onClick={() => setRootBlocksOpen(!rootBlocksOpen)}
                                >
                                    <span className={cl.chevron} style={{ display: 'inline-flex', alignItems: 'center' }}>
                                        <Icon name={rootBlocksOpen ? 'chevronDown' : 'chevronRight'} size={10} strokeWidth={2.5} />
                                    </span>
                                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                                        <Icon name="boxes" size={13} /> Блоки ({rootBlocks.length})
                                    </span>
                                </div>
                                {rootBlocksOpen && (
                                    <>
                                        {rootBlocks.slice(0, 80).map(b => renderBlockItem(b, 0))}
                                        {rootBlocks.length > 80 && (
                                            <div className={cl.moreNote}>... и ещё {rootBlocks.length - 80}</div>
                                        )}
                                    </>
                                )}
                            </>
                        )}

                        {/* Свободные примитивы в корне */}
                        {rootPrims.length > 0 && (
                            <>
                                <div
                                    className={cl.groupHeader}
                                    onClick={() => setRootPrimsOpen(!rootPrimsOpen)}
                                >
                                    <span className={cl.chevron} style={{ display: 'inline-flex', alignItems: 'center' }}>
                                        <Icon name={rootPrimsOpen ? 'chevronDown' : 'chevronRight'} size={10} strokeWidth={2.5} />
                                    </span>
                                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                                        <Icon name="cube" size={13} /> Примитивы ({rootPrims.length})
                                    </span>
                                </div>
                                {rootPrimsOpen && rootPrims.map(p => renderPrimitiveItem(p, 0))}
                            </>
                        )}

                        {/* Свободные модели в корне */}
                        {rootModels.length > 0 && (
                            <>
                                <div
                                    className={cl.groupHeader}
                                    onClick={() => setRootModelsOpen(!rootModelsOpen)}
                                >
                                    <span className={cl.chevron} style={{ display: 'inline-flex', alignItems: 'center' }}>
                                        <Icon name={rootModelsOpen ? 'chevronDown' : 'chevronRight'} size={10} strokeWidth={2.5} />
                                    </span>
                                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                                        <Icon name="trees" size={13} /> Модели ({rootModels.length})
                                    </span>
                                </div>
                                {rootModelsOpen && rootModels.map(m => renderModelItem(m, 0))}
                            </>
                        )}

                        {/* Мои модели (воксельный редактор) в корне */}
                        {rootUserModels.length > 0 && (
                            <>
                                <div
                                    className={cl.groupHeader}
                                    onClick={() => setRootUserModelsOpen(!rootUserModelsOpen)}
                                >
                                    <span className={cl.chevron} style={{ display: 'inline-flex', alignItems: 'center' }}>
                                        <Icon name={rootUserModelsOpen ? 'chevronDown' : 'chevronRight'} size={10} strokeWidth={2.5} />
                                    </span>
                                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                                        <Icon name="cube" size={13} /> Мои модели ({rootUserModels.length})
                                    </span>
                                </div>
                                {rootUserModelsOpen && rootUserModels.map(um => renderUserModelItem(um, 0))}
                            </>
                        )}
                    </div>
                )}

                {/* === 🌞 ОСВЕЩЕНИЕ === */}
                <GroupRow
                    icon="🌞"
                    label="Освещение"
                    open={lightingOpen}
                    onToggle={() => setLightingOpen(!lightingOpen)}
                />
                {lightingOpen && (
                    <div style={{ paddingLeft: 8 }}>
                        <div
                            className={`${cl.item} ${selection?.type === 'lighting' ? cl.itemSelected : ''}`}
                            onClick={() => onSelectLighting?.()}
                            title="Настройки света, теней и тумана"
                        >
                            <span className={cl.itemIcon} style={{ display: 'inline-flex' }}><Icon name="sun" size={14} /></span>
                            <span className={cl.itemLabel}>Свет и атмосфера</span>
                        </div>
                    </div>
                )}

                {/* === 🎵 ЗВУК === */}
                <GroupRow
                    icon="🎵"
                    label="Звук"
                    open={soundOpen}
                    onToggle={() => setSoundOpen(!soundOpen)}
                />
                {soundOpen && (
                    <div style={{ paddingLeft: 8 }}>
                        <div
                            className={`${cl.item} ${selection?.type === 'sound' ? cl.itemSelected : ''}`}
                            onClick={() => onSelectSound?.()}
                            title="Амбиент и музыка"
                        >
                            <span className={cl.itemIcon} style={{ display: 'inline-flex' }}><Icon name="music" size={14} /></span>
                            <span className={cl.itemLabel}>Амбиент и музыка</span>
                        </div>
                        {/* Свои звуки (Фаза 5.5) — загруженные mp3/wav. */}
                        {soundList.map(snd => (
                            <div
                                key={snd.id}
                                className={cl.item}
                                title={`${snd.name} (${snd.id}) — для game.sound.play('${snd.id}')`}
                            >
                                <span className={cl.itemIcon} style={{ display: 'inline-flex' }}>
                                    <Icon name="music" size={13} />
                                </span>
                                <span className={cl.itemLabel} style={{ flex: 1 }}>
                                    {snd.name} <span style={{ opacity: 0.5, fontSize: 10 }}>{snd.id}</span>
                                </span>
                                <button
                                    onClick={(e) => { e.stopPropagation(); onRemoveSound?.(snd.id); }}
                                    title="Удалить звук"
                                    style={{
                                        width: 16, height: 16, borderRadius: 8, border: 'none',
                                        background: '#c0392b', color: '#fff', fontSize: 10,
                                        lineHeight: '16px', cursor: 'pointer', padding: 0,
                                    }}
                                >×</button>
                            </div>
                        ))}
                        <button
                            onClick={() => soundFileRef.current?.click()}
                            style={{
                                width: 'calc(100% - 4px)', margin: '4px 2px 2px',
                                padding: '5px 8px', borderRadius: 4,
                                border: '1px dashed var(--accent, #3357ff)',
                                background: 'transparent', color: 'var(--accent, #3357ff)',
                                cursor: 'pointer', fontSize: 11,
                            }}
                        >
                            + Загрузить звук
                        </button>
                        <input
                            ref={soundFileRef}
                            type="file"
                            accept="audio/mpeg,audio/wav,audio/ogg,audio/*"
                            style={{ display: 'none' }}
                            onChange={(e) => {
                                const f = e.target.files && e.target.files[0];
                                e.target.value = '';
                                if (f) onUploadSound?.(f);
                            }}
                        />
                    </div>
                )}

                {/* === 👤 ИГРОК === */}
                <GroupRow
                    icon="👤"
                    label="Игрок"
                    open={playerOpen}
                    onToggle={() => setPlayerOpen(!playerOpen)}
                />
                {playerOpen && (
                    <div style={{ paddingLeft: 8 }}>
                        <div
                            className={`${cl.item} ${selection?.type === 'player' ? cl.itemSelected : ''}`}
                            onClick={() => onSelectPlayer?.()}
                            title="Скин игрока"
                        >
                            <span className={cl.itemIcon} style={{ display: 'inline-flex' }}><Icon name="user" size={14} /></span>
                            <span className={cl.itemLabel}>Скин игрока</span>
                        </div>
                        <div
                            className={`${cl.item} ${selection?.type === 'playerProps' ? cl.itemSelected : ''}`}
                            onClick={() => onSelectPlayerProps?.()}
                            title="Свойства игрока (прыжок, прицел)"
                        >
                            <span className={cl.itemIcon} style={{ display: 'inline-flex' }}><Icon name="settings" size={14} /></span>
                            <span className={cl.itemLabel}>Свойства игрока</span>
                        </div>
                    </div>
                )}

                {/* === 🎨 ИНТЕРФЕЙС === — 2D-UI поверх сцены (Frame/Text/Button/Image) */}
                <div style={{ position: 'relative' }}>
                    <GroupRow
                        icon="🎨"
                        label={`Интерфейс (${guiElements.length})`}
                        open={guiOpen}
                        onToggle={() => setGuiOpen(!guiOpen)}
                        plusItems={[
                            { id: 'add-frame',   label: 'Контейнер',  icon: '🟧',
                              onClick: () => { onCreateGui?.('frame'); setGuiOpen(true); } },
                            { id: 'add-scroll',  label: 'Список',     icon: '📜',
                              onClick: () => { onCreateGui?.('scroll'); setGuiOpen(true); } },
                            { id: 'add-text',    label: 'Надпись',    icon: 'type',
                              onClick: () => { onCreateGui?.('text'); setGuiOpen(true); } },
                            { id: 'add-button',  label: 'Кнопка',     icon: '🟢',
                              onClick: () => { onCreateGui?.('button'); setGuiOpen(true); } },
                            { id: 'add-textbox', label: 'Поле ввода', icon: '🔤',
                              onClick: () => { onCreateGui?.('textbox'); setGuiOpen(true); } },
                            { id: 'add-image',   label: 'Картинка',   icon: '🖼',
                              onClick: () => { onCreateGui?.('image'); setGuiOpen(true); } },
                        ]}
                    />
                    {/* Глазок — временно скрыть весь GUI в редакторе.
                        В Play-режиме всё равно видно. */}
                    {onToggleGuiOverlay && (
                        <button
                            onClick={(e) => { e.stopPropagation(); onToggleGuiOverlay(); }}
                            title={guiOverlayHidden
                                ? 'Показать интерфейс в редакторе'
                                : 'Скрыть интерфейс в редакторе'}
                            style={{
                                position: 'absolute',
                                right: 30, top: 4,
                                width: 22, height: 22,
                                display: 'flex', alignItems: 'center', justifyContent: 'center',
                                background: guiOverlayHidden ? 'rgba(245, 158, 11, 0.22)' : 'transparent',
                                border: '1px solid ' + (guiOverlayHidden ? '#f59e0b' : '#4a4a4a'),
                                borderRadius: 4,
                                cursor: 'pointer',
                                fontSize: 13,
                                color: guiOverlayHidden ? '#f5b342' : '#9a9a9e',
                                padding: 0,
                                userSelect: 'none',
                            }}
                        >
                            <Icon name={guiOverlayHidden ? 'eye-off' : 'eye'} size={13} />
                        </button>
                    )}
                </div>
                {guiOpen && (
                    <div style={{ paddingLeft: 8 }}>
                        {guiElements.length === 0 && (
                            <div style={{ padding: '4px 10px', fontSize: 11, color: '#94a3b8', fontStyle: 'italic' }}>
                                Пусто. Жми «+» чтобы создать.
                            </div>
                        )}
                        {/* Рендер дерева GUI начиная с корня (parentId=null).
                            Каждый Frame показывает своих детей с отступом. */}
                        {(() => {
                            const renderGuiTree = (parentId, depth) => {
                                const list = guiElements
                                    .filter(g => (g.parentId || null) === parentId)
                                    .slice()
                                    .sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));
                                return list.map(el => {
                                    const isSel = selection?.type === 'gui' && selection.id === el.id;
                                    const icon = el.type === 'frame' ? '🟧'
                                               : el.type === 'scroll' ? '📜'
                                               : el.type === 'text' ? 'type'
                                               : el.type === 'button' ? '🟢'
                                               : el.type === 'textbox' ? '🔤'
                                               : el.type === 'image' ? '🖼' : 'circle';
                                    const nested = scripts.filter(s => targetMatches(s.target, 'gui', el.id));
                                    const hasChildren = guiElements.some(g => g.parentId === el.id);
                                    return (
                                        <React.Fragment key={el.id}>
                                            <div
                                                className={`${cl.item} ${isSel ? cl.itemSelected : ''}`}
                                                onClick={() => onSelectGui?.(el.id)}
                                                onContextMenu={(e) => handleContextMenu(e, {
                                                    type: 'gui', id: el.id, name: el.name,
                                                    guiType: el.type,
                                                })}
                                                title={el.name}
                                                style={{ opacity: el.visible === false ? 0.5 : 1, paddingLeft: 8 + depth * 14 }}
                                                draggable={!isSel ? false : true}
                                                onDragStart={(e) => {
                                                    e.dataTransfer.effectAllowed = 'move';
                                                    e.dataTransfer.setData('text/gui-id', el.id);
                                                }}
                                                onDragOver={(e) => {
                                                    if (el.type !== 'frame') return;
                                                    const draggedId = e.dataTransfer.types.includes('text/gui-id');
                                                    if (!draggedId) return;
                                                    e.preventDefault();
                                                    e.dataTransfer.dropEffect = 'move';
                                                }}
                                                onDrop={(e) => {
                                                    if (el.type !== 'frame') return;
                                                    const draggedId = e.dataTransfer.getData('text/gui-id');
                                                    if (!draggedId || draggedId === el.id) return;
                                                    e.preventDefault();
                                                    onSetGuiParent?.(draggedId, el.id);
                                                }}
                                            >
                                                <span className={cl.itemIcon} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
                        {renderRowIcon(hasChildren ? '📂' : icon)}
                    </span>
                                                <span className={cl.itemLabel}>{el.name}</span>
                                            </div>
                                            {/* Вложенные скрипты этого элемента */}
                                            {nested.map(s => renderScript(s, depth + 1))}
                                            {/* Дочерние GUI-элементы */}
                                            {renderGuiTree(el.id, depth + 1)}
                                        </React.Fragment>
                                    );
                                });
                            };
                            return renderGuiTree(null, 0);
                        })()}
                        {/* Drop-zone в конце для перетаскивания на корень */}
                        <div
                            onDragOver={(e) => {
                                if (e.dataTransfer.types.includes('text/gui-id')) e.preventDefault();
                            }}
                            onDrop={(e) => {
                                const draggedId = e.dataTransfer.getData('text/gui-id');
                                if (!draggedId) return;
                                e.preventDefault();
                                onSetGuiParent?.(draggedId, null);
                            }}
                            style={{ height: 6 }}
                        />
                    </div>
                )}

                {/* === 📜 СКРИПТЫ === — только глобальные (без target).
                    Скрипты с target отображаются под объектом-носителем. */}
                {(() => {
                    // Глобальные скрипты: без target ИЛИ target==='game' (строка).
                    // Раньше фильтр был `!s.target` → скрипты с target:'game'
                    // (главный скрипт игры) НЕ показывались в дереве и их нельзя
                    // было удалить, хотя в Play они исполнялись.
                    const isGlobalTarget = (t) => !t || t === 'game';
                    const globalScripts = scripts.filter(s => isGlobalTarget(s.target));
                    return (
                        <>
                            <GroupRow
                                icon="📜"
                                label={`Скрипты (${globalScripts.length})`}
                                open={scriptsOpen}
                                onToggle={() => setScriptsOpen(!scriptsOpen)}
                                plusItems={[
                                    {
                                        id: 'add-script', label: 'Новый скрипт', icon: '📜',
                                        onClick: () => onCreateScript?.(null),
                                    },
                                ]}
                            />
                            {scriptsOpen && (
                                <div style={{ paddingLeft: 8 }}>
                                    {globalScripts.length === 0 ? (
                                        <div style={{ padding: '6px 12px', fontSize: 11, color: 'var(--text-dim)', fontStyle: 'italic' }}>
                                            Нет глобальных скриптов
                                        </div>
                                    ) : (
                                        globalScripts.map(s => renderScript(s, 0))
                                    )}
                                </div>
                            )}
                        </>
                    );
                })()}
            </div>

            {/* Контекст-меню */}
            {contextMenu && (
                <div
                    className={cl.contextMenu}
                    style={{ left: contextMenu.x, top: contextMenu.y }}
                    onClick={(e) => e.stopPropagation()}
                >
                    {contextMenu.item.type === 'gui' ? (
                        <>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    onSelectGui?.(contextMenu.item.id);
                                    closeContext();
                                }}
                            >
                                <Icon name="target" size={13} /> Выделить
                            </div>
                            {contextMenu.item.guiType === 'frame' && (
                                <>
                                    <div
                                        className={cl.contextItem}
                                        onClick={() => {
                                            onCreateGui?.('frame', { parentId: contextMenu.item.id });
                                            closeContext();
                                        }}
                                    >
                                        <Icon name="square" size={13} /> + Контейнер внутри
                                    </div>
                                    <div
                                        className={cl.contextItem}
                                        onClick={() => {
                                            onCreateGui?.('text', { parentId: contextMenu.item.id });
                                            closeContext();
                                        }}
                                    >
                                        <Icon name="type" size={13} /> + Надпись внутри
                                    </div>
                                    <div
                                        className={cl.contextItem}
                                        onClick={() => {
                                            onCreateGui?.('button', { parentId: contextMenu.item.id });
                                            closeContext();
                                        }}
                                    >
                                        <Icon name="circle" size={13} /> + Кнопка внутри
                                    </div>
                                    <div
                                        className={cl.contextItem}
                                        onClick={() => {
                                            onCreateGui?.('image', { parentId: contextMenu.item.id });
                                            closeContext();
                                        }}
                                    >
                                        <Icon name="image" size={13} /> + Картинка внутри
                                    </div>
                                </>
                            )}
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    const newId = onCreateScript?.({ kind: 'gui', id: contextMenu.item.id });
                                    if (newId != null) setGuiOpen(true);
                                    closeContext();
                                }}
                            >
                                <Icon name="script" size={13} /> Добавить скрипт
                            </div>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    const cur = (contextMenu.item.name || '').trim();
                                    const next = window.prompt('Новое имя элемента:', cur);
                                    if (next && next !== cur) onRenameGui?.(contextMenu.item.id, next);
                                    closeContext();
                                }}
                            >
                                <Icon name="rename" size={13} /> Переименовать
                            </div>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    onMoveGuiZ?.(contextMenu.item.id, 'up');
                                    closeContext();
                                }}
                            >
                                <Icon name="arrow-up" size={13} /> Поднять
                            </div>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    onMoveGuiZ?.(contextMenu.item.id, 'down');
                                    closeContext();
                                }}
                            >
                                <Icon name="arrow-down" size={13} /> Опустить
                            </div>
                            <div
                                className={`${cl.contextItem} ${cl.contextDanger}`}
                                onClick={() => {
                                    onDeleteGui?.(contextMenu.item.id);
                                    closeContext();
                                }}
                            >
                                <Icon name="delete" size={13} /> Удалить
                            </div>
                        </>
                    ) : contextMenu.item.type === 'floor' ? (
                        <>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    onSelectFloor?.();
                                    closeContext();
                                }}
                            >
                                <Icon name="target" size={13} /> Выделить
                            </div>
                            <div
                                className={`${cl.contextItem} ${cl.contextDanger}`}
                                onClick={() => {
                                    onDeleteFloor?.();
                                    closeContext();
                                }}
                            >
                                <Icon name="delete" size={13} /> Удалить пол
                            </div>
                        </>
                    ) : contextMenu.item.type === 'spawn' ? (
                        <>
                            <div
                                className={cl.contextItem}
                                onClick={() => { onSelectSpawn?.(); onFocusSelection?.(); closeContext(); }}
                            >
                                <Icon name="target" size={13} /> Навести камеру
                            </div>
                            <div
                                className={`${cl.contextItem} ${cl.contextDanger}`}
                                onClick={() => { onDeleteSpawn?.(); closeContext(); }}
                            >
                                <Icon name="delete" size={13} /> Удалить точку спавна
                            </div>
                        </>
                    ) : contextMenu.item.type === 'script' ? (
                        <>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    onSelectScript?.(contextMenu.item.id);
                                    closeContext();
                                }}
                            >
                                <Icon name="folder-open" size={13} /> Открыть
                            </div>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    startRename('script', contextMenu.item.id, contextMenu.item.name);
                                    closeContext();
                                }}
                            >
                                <Icon name="rename" size={13} /> Переименовать
                            </div>
                            <div
                                className={`${cl.contextItem} ${cl.contextDanger}`}
                                onClick={() => {
                                    onDeleteScript?.(contextMenu.item.id);
                                    closeContext();
                                }}
                            >
                                <Icon name="delete" size={13} /> Удалить
                            </div>
                        </>
                    ) : contextMenu.item.type === 'folder' ? (
                        <>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    const newId = onCreateFolder?.('Новая папка', contextMenu.item.id);
                                    if (newId != null) {
                                        setOpenFolders(prev => new Set(prev).add(contextMenu.item.id));
                                    }
                                    closeContext();
                                }}
                            >
                                <Icon name="folder-add" size={13} /> Подпапка
                            </div>
                            <div
                                className={cl.contextItem}
                                onClick={() => startRenameFolder(contextMenu.item)}
                            >
                                <Icon name="rename" size={13} /> Переименовать
                            </div>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    onSetFolderVisible?.(contextMenu.item.id, !contextMenu.item.visible);
                                    closeContext();
                                }}
                            >
                                <Icon name={contextMenu.item.visible ? 'hidden' : 'visible'} size={13} />
                                {contextMenu.item.visible ? ' Скрыть' : ' Показать'}
                            </div>
                            <div
                                className={`${cl.contextItem} ${cl.contextDanger}`}
                                onClick={() => {
                                    onRemoveFolder?.(contextMenu.item.id, false);
                                    closeContext();
                                }}
                                title="Содержимое останется на сцене"
                            >
                                <Icon name="delete" size={13} /> Удалить папку
                            </div>
                            <div
                                className={`${cl.contextItem} ${cl.contextDanger}`}
                                onClick={() => {
                                    if (window.confirm('Удалить папку и всё её содержимое?')) {
                                        onRemoveFolder?.(contextMenu.item.id, true);
                                    }
                                    closeContext();
                                }}
                            >
                                <Icon name="bomb" size={13} /> Удалить с содержимым
                            </div>
                        </>
                    ) : (
                        <>
                            <div
                                className={cl.contextItem}
                                onClick={() => {
                                    const it = contextMenu.item;
                                    if (it.type === 'block') onSelectBlock(it.gridX, it.gridY, it.gridZ);
                                    else if (it.type === 'primitive') onSelectPrimitive?.(it.id);
                                    else onSelectModel(it.instanceId);
                                    onFocusSelection?.();
                                    closeContext();
                                }}
                            >
                                <Icon name="target" size={13} /> Сфокусировать
                            </div>
                            {/* Переименование — только для моделей и примитивов (блоков много, не имеет смысла) */}
                            {(contextMenu.item.type === 'model' || contextMenu.item.type === 'primitive') && (
                                <div
                                    className={cl.contextItem}
                                    onClick={() => {
                                        const it = contextMenu.item;
                                        if (it.type === 'model') {
                                            const mt = getModelType(it.modelTypeId);
                                            startRename('model', String(it.instanceId), it.name || mt?.name || it.modelTypeId);
                                        } else {
                                            const def = getPrimitiveType(it.type);
                                            startRename('primitive', String(it.id), it.name || def?.name || it.type);
                                        }
                                        closeContext();
                                    }}
                                >
                                    <Icon name="rename" size={13} /> Переименовать
                                </div>
                            )}
                            {/* Подменю «Переместить в папку» */}
                            {folders.length > 0 && (
                                <div className={cl.contextSubmenu}>
                                    <div className={cl.contextSubmenuTitle} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                                        <Icon name="folder" size={13} /> Переместить в...
                                    </div>
                                    <div
                                        className={cl.contextItem}
                                        onClick={() => {
                                            const it = contextMenu.item;
                                            if (it.type === 'block') onAssignToFolder?.('block', { x: it.gridX, y: it.gridY, z: it.gridZ }, null);
                                            else if (it.type === 'primitive') onAssignToFolder?.('primitive', it.id, null);
                                            else if (it.type === 'model') onAssignToFolder?.('model', it.instanceId, null);
                                            closeContext();
                                        }}
                                    >
                                        <Icon name="globe" size={13} /> Корень
                                    </div>
                                    {folders.map(f => (
                                        <div
                                            key={f.id}
                                            className={cl.contextItem}
                                            onClick={() => {
                                                const it = contextMenu.item;
                                                if (it.type === 'block') onAssignToFolder?.('block', { x: it.gridX, y: it.gridY, z: it.gridZ }, f.id);
                                                else if (it.type === 'primitive') onAssignToFolder?.('primitive', it.id, f.id);
                                                else if (it.type === 'model') onAssignToFolder?.('model', it.instanceId, f.id);
                                                closeContext();
                                            }}
                                        >
                                            <Icon name="folder" size={13} /> {f.name}
                                        </div>
                                    ))}
                                </div>
                            )}
                            <div
                                className={`${cl.contextItem} ${cl.contextDanger}`}
                                onClick={() => {
                                    const it = contextMenu.item;
                                    if (it.type === 'block') onDeleteBlock(it.gridX, it.gridY, it.gridZ);
                                    else if (it.type === 'primitive') onDeletePrimitive?.(it.id);
                                    else onDeleteModel(it.instanceId);
                                    closeContext();
                                }}
                            >
                                <Icon name="delete" size={13} /> Удалить
                            </div>
                        </>
                    )}
                </div>
            )}
        </div>
    );
};

export default HierarchyPanel;
