import React from 'react';

/**
 * GuiOverlay — рендерит GUI-элементы (Frame/Text/Button/Image) поверх viewport.
 *
 * Этап 2.8.1: drag-and-drop, выделение.
 * Этап 2.8.2: resize-ручки (8 точек: углы и середины сторон), snap-линии
 *             к центру/краям/середине viewport и к границам других элементов.
 *
 * В Play-режиме:
 *   - элементы рендерятся «как есть»,
 *   - кнопки кликабельны (на этапе 2.8.3 будут шлёт событие в скрипты).
 */

const SNAP_THRESHOLD = 1.5; // % — расстояние, в пределах которого срабатывает snap

function GuiOverlay({
    elements = [],
    isPlaying = false,
    selectedId = null,
    onSelect,
    onUpdate,
    onDelete,
    onPlayClick,
    containerRef,
    // resolveAsset(id) → dataURL картинки из AssetManager (для image-GUI).
    resolveAsset,
}) {
    // snapLines — массив { dir: 'v'|'h', pos: % } для отрисовки направляющих во время drag/resize
    const [snapLines, setSnapLines] = React.useState([]);

    if (!Array.isArray(elements) || elements.length === 0) {
        return null;
    }

    // Корневые элементы (parentId == null) — рендерятся в overlay-контейнер.
    // Дочерние рендерятся внутри своего родителя через рекурсивный GuiElement.
    const rootSorted = elements
        .filter(e => !e.parentId)
        .sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));

    // Map id → children для быстрого поиска
    const childrenMap = new Map();
    for (const el of elements) {
        if (!el.parentId) continue;
        if (!childrenMap.has(el.parentId)) childrenMap.set(el.parentId, []);
        childrenMap.get(el.parentId).push(el);
    }
    for (const arr of childrenMap.values()) {
        arr.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));
    }

    return (
        <div
            style={{
                position: 'absolute',
                inset: 0,
                pointerEvents: 'none',
                zIndex: 25,
                overflow: 'hidden',
            }}
        >
            {rootSorted.map((el) => (
                <GuiElement
                    key={el.id}
                    el={el}
                    allElements={elements}
                    childrenMap={childrenMap}
                    isPlaying={isPlaying}
                    selectedId={selectedId}
                    onSelect={onSelect}
                    onUpdate={onUpdate}
                    onDelete={onDelete}
                    onPlayClick={onPlayClick}
                    containerRef={containerRef}
                    setSnapLines={setSnapLines}
                    resolveAsset={resolveAsset}
                />
            ))}
            {/* Snap-направляющие (рендерятся только для корневых элементов) */}
            {!isPlaying && snapLines.map((line, i) => (
                <div key={i} style={{
                    position: 'absolute',
                    background: '#66b04a',
                    opacity: 0.85,
                    pointerEvents: 'none',
                    zIndex: 100,
                    boxShadow: '0 0 4px rgba(102, 176, 74, 0.7)',
                    ...(line.dir === 'v'
                        ? { left: `${line.pos}%`, top: 0, bottom: 0, width: 1 }
                        : { top: `${line.pos}%`, left: 0, right: 0, height: 1 }),
                }} />
            ))}
        </div>
    );
}

function GuiElement({ el, allElements, childrenMap, isPlaying, selectedId, onSelect, onUpdate, onDelete, onPlayClick, containerRef, setSnapLines, resolveAsset }) {
    const [dragging, setDragging] = React.useState(false);
    const [resizing, setResizing] = React.useState(null); // 'n'|'s'|'e'|'w'|'ne'|'nw'|'se'|'sw' или null
    const [hover, setHover] = React.useState(false);
    const [pressed, setPressed] = React.useState(false);
    const selfRef = React.useRef(null);

    const selected = !isPlaying && selectedId === el.id;
    const myChildren = childrenMap?.get(el.id) || [];

    if (el.visible === false && !isPlaying && !selected) return null;
    if (el.visible === false && isPlaying) return null;

    const style = elementToStyle(el);

    /** Начало drag (перемещение всего элемента). */
    const startDrag = (e) => {
        if (isPlaying || e.button !== 0) return;
        e.stopPropagation();
        e.preventDefault();
        onSelect?.(el.id);
        // Координаты считаем относительно родительского DOM:
        // - для root-элементов это viewport (containerRef)
        // - для дочерних — DOM-узел Frame'а-родителя.
        // Используем parentNode т.к. selfRef.current.parentNode и есть наш контейнер
        // (обёртка-родитель в DOM).
        const container = el.parentId
            ? selfRef.current?.parentNode
            : containerRef?.current;
        if (!container) return;
        const rect = container.getBoundingClientRect();
        const startMouseX = e.clientX;
        const startMouseY = e.clientY;
        const startX = el.x ?? 50;
        const startY = el.y ?? 50;
        setDragging(true);
        // Знаки для якорей: позиция X/Y отсчитывается ОТ соответствующего края.
        // Для bottom-* — Y растёт ВВЕРХ от низа, поэтому движение мыши вниз
        // (положительный dyPct) должно уменьшать Y. То же для right-* по X.
        const anchor = el.anchor || 'center';
        const xSign = anchor.endsWith('-right') ? -1 : 1;
        const ySign = anchor.startsWith('bottom-') ? -1 : 1;
        const onMove = (ev) => {
            const dxPct = ((ev.clientX - startMouseX) / rect.width) * 100;
            const dyPct = ((ev.clientY - startMouseY) / rect.height) * 100;
            const step = ev.shiftKey ? 5 : 1;
            let nx = Math.max(0, Math.min(100, startX + dxPct * xSign));
            let ny = Math.max(0, Math.min(100, startY + dyPct * ySign));
            // Snap работает только для корневых элементов (внутри viewport).
            // Для дочерних — только округление, без snap-линий.
            if (!el.parentId) {
                const snaps = computeSnap(el, allElements, nx, ny, el.w, el.h);
                if (snaps.x != null) nx = snaps.x;
                if (snaps.y != null) ny = snaps.y;
                setSnapLines(snaps.lines || []);
            }
            // Округление к целым (Shift = 5%)
            nx = Math.round(nx / step) * step;
            ny = Math.round(ny / step) * step;
            onUpdate?.(el.id, { x: nx, y: ny });
        };
        const onUp = () => {
            setDragging(false);
            setSnapLines([]);
            window.removeEventListener('mousemove', onMove);
            window.removeEventListener('mouseup', onUp);
        };
        window.addEventListener('mousemove', onMove);
        window.addEventListener('mouseup', onUp);
    };

    /** Начало resize за одну из 8 ручек. */
    const startResize = (handle) => (e) => {
        if (isPlaying || e.button !== 0) return;
        e.stopPropagation();
        e.preventDefault();
        const container = el.parentId
            ? selfRef.current?.parentNode
            : containerRef?.current;
        if (!container) return;
        const rect = container.getBoundingClientRect();
        const startMouseX = e.clientX;
        const startMouseY = e.clientY;
        const startX = el.x ?? 50;
        const startY = el.y ?? 50;
        const startW = el.w ?? 20;
        const startH = el.h ?? 10;
        const anchor = el.anchor || 'center';
        setResizing(handle);
        const onMove = (ev) => {
            const dxPct = ((ev.clientX - startMouseX) / rect.width) * 100;
            const dyPct = ((ev.clientY - startMouseY) / rect.height) * 100;
            const step = ev.shiftKey ? 5 : 1;
            let nx = startX, ny = startY, nw = startW, nh = startH;

            // Считаем что Y «направлен» от ССЫЛОЧНОГО края к противоположному:
            // top-* и center — Y растёт вниз (как мышь), bottom-* — вверх.
            // X аналогично: left-* и center — вправо (как мышь), right-* — влево.
            const xSign = anchor.endsWith('-right') ? -1 : 1;
            const ySign = anchor.startsWith('bottom-') ? -1 : 1;
            // dx/dy в координатах элемента (с учётом направления)
            const adx = dxPct * xSign;
            const ady = dyPct * ySign;
            // Какой край тянем в координатах ЭЛЕМЕНТА (не мыши)
            // 'e' — мышь вправо, для left-якорей это правый край элемента,
            // для right-якорей — левый край (мы дёргаем дальний от ссылки край).
            const isPosEdgeX = anchor.endsWith('-right') ? handle.includes('w') : handle.includes('e');
            const isNegEdgeX = anchor.endsWith('-right') ? handle.includes('e') : handle.includes('w');
            const isPosEdgeY = anchor.startsWith('bottom-') ? handle.includes('n') : handle.includes('s');
            const isNegEdgeY = anchor.startsWith('bottom-') ? handle.includes('s') : handle.includes('n');

            // Изменение размера и позиции в координатах элемента
            if (anchor === 'center') {
                // Центрированный якорь: тянем любой край → центр движется на половину дельты
                if (handle.includes('e')) { nw = startW + dxPct; nx = startX + dxPct / 2; }
                if (handle.includes('w')) { nw = startW - dxPct; nx = startX + dxPct / 2; }
                if (handle.includes('s')) { nh = startH + dyPct; ny = startY + dyPct / 2; }
                if (handle.includes('n')) { nh = startH - dyPct; ny = startY + dyPct / 2; }
            } else {
                // Угловые якоря: ближняя к якорю сторона = позиция (фиксирована),
                // дальняя сторона = позиция + размер.
                if (isPosEdgeX) {
                    // Тянем «дальний» край — меняется только размер
                    nw = Math.max(2, startW + adx);
                    nx = startX;
                } else if (isNegEdgeX) {
                    // Тянем «ближний» край — двигается и позиция, и размер
                    nx = startX + adx;
                    nw = Math.max(2, startW - adx);
                }
                if (isPosEdgeY) {
                    nh = Math.max(2, startH + ady);
                    ny = startY;
                } else if (isNegEdgeY) {
                    ny = startY + ady;
                    nh = Math.max(2, startH - ady);
                }
            }

            // Защита от отрицательных размеров (для center)
            nw = Math.max(2, nw);
            nh = Math.max(2, nh);

            // Округление до целых процентов
            nx = Math.max(0, Math.min(100, Math.round(nx / step) * step));
            ny = Math.max(0, Math.min(100, Math.round(ny / step) * step));
            nw = Math.max(2, Math.min(100, Math.round(nw / step) * step));
            nh = Math.max(2, Math.min(100, Math.round(nh / step) * step));

            onUpdate?.(el.id, { x: nx, y: ny, w: nw, h: nh });
        };
        const onUp = () => {
            setResizing(null);
            window.removeEventListener('mousemove', onMove);
            window.removeEventListener('mouseup', onUp);
        };
        window.addEventListener('mousemove', onMove);
        window.addEventListener('mouseup', onUp);
    };

    const isButton = el.type === 'button';
    const isImage = el.type === 'image';
    const isTextbox = el.type === 'textbox';
    const isScroll = el.type === 'scroll';
    const isText = el.type === 'text' || el.type === 'button';

    // textbox в Play кликабелен (для фокуса и ввода), как и кнопка
    const pointerEvents = isPlaying ? ((isButton || isTextbox) ? 'auto' : 'none') : 'auto';

    // В Play на кнопке — hover/pressed эффект (Задача 03).
    // Если у элемента задан el.hover/el.active — используем их параметры,
    // иначе дефолтные значения.
    const playInteractive = isPlaying && isButton;
    const hoverCfg = el.hover || { scale: 1.08, brightness: 1.15, rotation: 0 };
    const activeCfg = el.active || { scale: 0.94 };
    const hoverBrightness = (typeof hoverCfg.brightness === 'number') ? hoverCfg.brightness : 1.15;
    const hoverScale = (typeof hoverCfg.scale === 'number') ? hoverCfg.scale : 1.08;
    const hoverRotation = (typeof hoverCfg.rotation === 'number') ? hoverCfg.rotation : 0;
    const activeScale = (typeof activeCfg.scale === 'number') ? activeCfg.scale : 0.94;
    const playFilter = pressed
        ? 'brightness(0.85)'
        : (hover ? `brightness(${hoverBrightness})` : 'none');
    const dynScale = pressed ? activeScale : (hover ? hoverScale : 1);
    const dynRot = hover ? hoverRotation : 0;
    let extraTr = '';
    if (dynScale !== 1) extraTr += ` scale(${dynScale})`;
    if (dynRot) extraTr += ` rotate(${dynRot}deg)`;
    const playTransform = (style.transform || '') + extraTr;

    return (
        <div
            ref={selfRef}
            onMouseDown={(e) => {
                if (playInteractive) { setPressed(true); }
                startDrag(e);
            }}
            onMouseUp={() => { if (playInteractive) setPressed(false); }}
            onMouseEnter={() => { if (playInteractive) setHover(true); }}
            onMouseLeave={() => { if (playInteractive) { setHover(false); setPressed(false); } }}
            onClick={(e) => {
                if (!isPlaying) return;
                e.stopPropagation();
                if (el.type === 'button') onPlayClick?.(el.id);
            }}
            onContextMenu={(e) => {
                if (isPlaying) return;
                e.preventDefault();
                e.stopPropagation();
                onSelect?.(el.id);
            }}
            onWheel={(e) => {
                // Прокрутка scroll-контейнера колесом мыши.
                if (!isScroll) return;
                e.stopPropagation();
                const children = childrenMap?.get(el.id) || [];
                // Полная высота контента (в % размера экрана) при вертикальной раскладке.
                const gap = Number.isFinite(el.layoutGap) ? el.layoutGap : 2;
                const pad = Number.isFinite(el.layoutPad) ? el.layoutPad : 3;
                let contentH = pad * 2;
                for (const ch of children) contentH += (ch.h ?? 10) + gap;
                // Сколько контента не влезает = на сколько можно прокрутить.
                const maxScroll = Math.max(0, contentH - (el.h ?? 40));
                const cur = Number.isFinite(el.scrollY) ? el.scrollY : 0;
                // deltaY>0 (колесо вниз) — увеличиваем сдвиг.
                const next = Math.max(0, Math.min(maxScroll, cur + (e.deltaY > 0 ? 4 : -4)));
                if (next !== cur) onUpdate?.(el.id, { scrollY: next });
            }}
            style={{
                ...style,
                transform: playInteractive ? playTransform : style.transform,
                filter: playInteractive ? playFilter : 'none',
                pointerEvents: isScroll && isPlaying ? 'auto' : pointerEvents,
                // scroll-контейнер обрезает детей, выходящих за границу.
                overflow: isScroll ? 'hidden' : (style.overflow || 'visible'),
                cursor: isPlaying ? (isButton ? 'pointer' : 'default') : (dragging ? 'grabbing' : 'grab'),
                outline: selected ? '2px solid #66b04a' : (isPlaying ? 'none' : '1px dashed rgba(150,200,120,0.35)'),
                outlineOffset: selected ? 2 : 0,
                transition: dragging || resizing ? 'none' : 'outline-color 0.12s, filter 0.1s, transform 0.05s',
                userSelect: 'none',
            }}
            title={isPlaying ? undefined : `${el.name} — перетащи мышью, потяни за ручки чтобы изменить размер`}
        >
            {isImage && (() => {
                // imageAsset (картинка из AssetManager) приоритетнее imageUrl.
                const assetUrl = el.imageAsset && resolveAsset ? resolveAsset(el.imageAsset) : null;
                const src = assetUrl || el.imageUrl;
                if (!src) return null;
                return (
                    <img
                        src={src}
                        alt={el.name || ''}
                        draggable={false}
                        style={{
                            width: '100%', height: '100%', objectFit: 'contain',
                            pointerEvents: 'none', display: 'block',
                        }}
                        onError={(e) => { e.currentTarget.style.opacity = '0.3'; }}
                    />
                );
            })()}
            {isText && (el.text != null) && (() => {
                // Задача 03: text-stroke (обводка текста). Через -webkit-text-stroke
                // (хорошая поддержка, чётко на крупном шрифте) + paint-order
                // (stroke под fill чтобы текст не «сжимался»).
                const ts = el.textStroke;
                const strokeStyle = (ts && ts.color && Number.isFinite(ts.width))
                    ? {
                        WebkitTextStroke: `${ts.width}px ${ts.color}`,
                        paintOrder: 'stroke fill',
                    }
                    : null;
                return (
                    <div style={{
                        width: '100%', height: '100%',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: el.textAlign === 'left' ? 'flex-start'
                                       : el.textAlign === 'right' ? 'flex-end' : 'center',
                        color: el.textColor || '#f0e6d8',
                        fontSize: el.textSize || 16,
                        fontWeight: el.fontWeight || 500,
                        fontFamily: '"Roboto Condensed", system-ui, sans-serif',
                        padding: '4px 8px',
                        boxSizing: 'border-box',
                        textAlign: el.textAlign || 'center',
                        lineHeight: 1.2,
                        whiteSpace: 'pre-wrap',
                        wordBreak: 'break-word',
                        pointerEvents: 'none',
                        ...(strokeStyle || {}),
                    }}>
                        {el.text}
                    </div>
                );
            })()}
            {/* Задача 03: Бейдж в углу — отдельный absolute-элемент. */}
            {el.badge && (() => {
                const b = el.badge;
                const corner = b.corner || 'top-right';
                const cornerStyle = {
                    'top-right':    { top: -6, right: -6 },
                    'top-left':     { top: -6, left: -6 },
                    'bottom-right': { bottom: -6, right: -6 },
                    'bottom-left':  { bottom: -6, left: -6 },
                }[corner] || { top: -6, right: -6 };
                const icons = {
                    exclamation: '!',
                    star: '★',
                    plus: '+',
                    new: 'NEW',
                    sale: '%',
                };
                const text = b.text != null ? b.text : (icons[b.icon] || '!');
                const big = b.icon === 'new';
                return (
                    <div style={{
                        position: 'absolute',
                        ...cornerStyle,
                        minWidth: big ? 32 : 22,
                        height: 22,
                        padding: '0 6px',
                        background: b.color || '#fbbf24',
                        color: '#3a1a00',
                        borderRadius: big ? 6 : 11,
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: big ? 11 : 14,
                        fontWeight: 800,
                        fontFamily: '"Roboto Condensed", system-ui, sans-serif',
                        boxShadow: '0 2px 4px rgba(0,0,0,0.4)',
                        border: '2px solid #fff',
                        pointerEvents: 'none',
                        zIndex: 5,
                        transform: 'rotate(8deg)',
                    }}>{text}</div>
                );
            })()}

            {/* TextBox — настоящий <input> в Play (принимает ввод),
                в редакторе — статичный вид с placeholder. */}
            {isTextbox && isPlaying && (
                <input
                    type="text"
                    defaultValue={el.text || ''}
                    placeholder={el.placeholder || ''}
                    onChange={(e) => onPlayClick?.(el.id, 'textchange', e.target.value)}
                    onKeyDown={(e) => {
                        if (e.key === 'Enter') {
                            e.currentTarget.blur();
                            onPlayClick?.(el.id, 'submit', e.currentTarget.value);
                        }
                    }}
                    onClick={(e) => e.stopPropagation()}
                    style={{
                        width: '100%', height: '100%',
                        boxSizing: 'border-box',
                        background: 'transparent',
                        border: 'none', outline: 'none',
                        color: el.textColor || '#f0e6d8',
                        fontSize: el.textSize || 16,
                        fontWeight: el.fontWeight || 500,
                        fontFamily: '"Roboto Condensed", system-ui, sans-serif',
                        padding: '4px 10px',
                        textAlign: el.textAlign || 'left',
                    }}
                />
            )}
            {isTextbox && !isPlaying && (
                <div style={{
                    width: '100%', height: '100%',
                    display: 'flex', alignItems: 'center',
                    color: (el.text ? el.textColor : '#8a7c6a'),
                    fontSize: el.textSize || 16,
                    fontFamily: '"Roboto Condensed", system-ui, sans-serif',
                    padding: '4px 10px',
                    boxSizing: 'border-box',
                    fontStyle: el.text ? 'normal' : 'italic',
                    pointerEvents: 'none',
                }}>
                    {el.text || el.placeholder || 'Поле ввода'}
                </div>
            )}

            {/* Метка с именем над выделенным элементом */}
            {selected && !isPlaying && (
                <div style={{
                    position: 'absolute',
                    left: 0, bottom: '100%',
                    marginBottom: 4,
                    fontSize: 10,
                    fontWeight: 600,
                    color: '#fff',
                    background: '#66b04a',
                    padding: '1px 6px',
                    borderRadius: 3,
                    pointerEvents: 'none',
                    whiteSpace: 'nowrap',
                }}>
                    {el.name}
                </div>
            )}

            {/* Resize-ручки на выделенном элементе */}
            {selected && !isPlaying && (
                <>
                    {RESIZE_HANDLES.map(h => (
                        <div
                            key={h.dir}
                            onMouseDown={startResize(h.dir)}
                            style={{
                                position: 'absolute',
                                width: 10, height: 10,
                                background: '#66b04a',
                                border: '1.5px solid #fff',
                                borderRadius: 2,
                                boxSizing: 'border-box',
                                ...h.pos,
                                cursor: h.cursor,
                                zIndex: 10,
                                pointerEvents: 'auto',
                            }}
                        />
                    ))}
                </>
            )}

            {/* Дочерние элементы — рендерятся ВНУТРИ этого контейнера.
                Если у контейнера layout='vertical'/'horizontal' — детям
                подменяем x/y на вычисленные раскладкой (UIListLayout). */}
            {myChildren.length > 0 && layoutChildren(el, myChildren).map(child => (
                <GuiElement
                    key={child.id}
                    el={child}
                    allElements={allElements}
                    childrenMap={childrenMap}
                    isPlaying={isPlaying}
                    selectedId={selectedId}
                    onSelect={onSelect}
                    onUpdate={onUpdate}
                    onDelete={onDelete}
                    onPlayClick={onPlayClick}
                    containerRef={containerRef}
                    setSnapLines={setSnapLines}
                    resolveAsset={resolveAsset}
                />
            ))}
        </div>
    );
}

/**
 * Авто-раскладка детей контейнера (Фаза 5.3, UIListLayout-аналог).
 * Если у контейнера layout='vertical'/'horizontal' — возвращает копии
 * детей с вычисленными x/y (в % размера контейнера) и якорем top-left.
 * При layout='none' возвращает детей как есть.
 */
function layoutChildren(container, children) {
    const layout = container && container.layout;
    if (layout !== 'vertical' && layout !== 'horizontal' && layout !== 'grid') return children;
    const gap = Number.isFinite(container.layoutGap) ? container.layoutGap : 2;
    const pad = Number.isFinite(container.layoutPad) ? container.layoutPad : 3;
    // scrollY -- сдвиг прокрутки (для type='scroll').
    const scrollY = Number.isFinite(container.scrollY) ? container.scrollY : 0;

    // Phase 6.3.2: Grid layout -- авто-сетка с заданной шириной ячейки.
    // layoutCellW/H -- размер ячейки в %, layoutCols -- сколько колонок (если 0 -- авто).
    if (layout === 'grid') {
        const cellW = Number.isFinite(container.layoutCellW) ? container.layoutCellW : 18;
        const cellH = Number.isFinite(container.layoutCellH) ? container.layoutCellH : 18;
        const availW = 100 - pad * 2;
        // Авто-вычисление кол-ва колонок если не задано.
        let cols = Number(container.layoutCols) || 0;
        if (cols < 1) cols = Math.max(1, Math.floor((availW + gap) / (cellW + gap)));
        return children.map((ch, i) => {
            const row = Math.floor(i / cols);
            const col = i % cols;
            const nx = pad + col * (cellW + gap);
            const ny = pad + row * (cellH + gap) - scrollY;
            return { ...ch, x: nx, y: ny, w: cellW, h: cellH, anchor: 'top-left' };
        });
    }

    let cursor = pad;
    return children.map((ch) => {
        const w = ch.w ?? 20, h = ch.h ?? 10;
        let nx, ny;
        if (layout === 'vertical') {
            nx = pad;
            ny = cursor - scrollY;
            cursor += h + gap;
        } else {
            nx = cursor;
            ny = pad - scrollY;
            cursor += w + gap;
        }
        // Якорь top-left -- координаты считаются от левого-верхнего угла.
        return { ...ch, x: nx, y: ny, anchor: 'top-left' };
    });
}

const RESIZE_HANDLES = [
    { dir: 'nw', pos: { left: -5,    top: -5    }, cursor: 'nwse-resize' },
    { dir: 'n',  pos: { left: '50%', top: -5,    transform: 'translateX(-50%)' }, cursor: 'ns-resize' },
    { dir: 'ne', pos: { right: -5,   top: -5    }, cursor: 'nesw-resize' },
    { dir: 'e',  pos: { right: -5,   top: '50%', transform: 'translateY(-50%)' }, cursor: 'ew-resize' },
    { dir: 'se', pos: { right: -5,   bottom: -5 }, cursor: 'nwse-resize' },
    { dir: 's',  pos: { left: '50%', bottom: -5, transform: 'translateX(-50%)' }, cursor: 'ns-resize' },
    { dir: 'sw', pos: { left: -5,    bottom: -5 }, cursor: 'nesw-resize' },
    { dir: 'w',  pos: { left: -5,    top: '50%', transform: 'translateY(-50%)' }, cursor: 'ew-resize' },
];

/**
 * Считает snap-привязки во время drag.
 * Возвращает { x, y, lines }, где x/y — скорректированные координаты (или null если без snap),
 * а lines — массив { dir: 'v'|'h', pos: % } для отрисовки направляющих.
 *
 * Цели для привязки:
 *   - центр viewport (50%, 50%)
 *   - края viewport (0%, 100%)
 *   - центр и края других элементов
 *
 * Учитываем только anchor='center' для расчёта границ — для упрощения первой версии.
 * (Корректно работает для большинства случаев.)
 */
function computeSnap(self, allElements, x, y, w, h) {
    const lines = [];
    let snapX = null, snapY = null;

    // Кандидаты: центр + границы (left/right/top/bottom) текущего элемента
    const halfW = (w || 20) / 2;
    const halfH = (h || 10) / 2;
    // Точки которые мы хотим «прицелить» к чему-то на сцене
    const myXTargets = [
        { value: x,           kind: 'cx' },
        { value: x - halfW,   kind: 'left' },
        { value: x + halfW,   kind: 'right' },
    ];
    const myYTargets = [
        { value: y,           kind: 'cy' },
        { value: y - halfH,   kind: 'top' },
        { value: y + halfH,   kind: 'bottom' },
    ];

    // Цели-якоря на viewport
    const xAnchors = [0, 25, 50, 75, 100];
    const yAnchors = [0, 25, 50, 75, 100];

    // Цели от других элементов
    for (const other of allElements) {
        if (other.id === self.id) continue;
        if (other.visible === false) continue;
        if ((other.anchor || 'center') !== 'center') continue; // упрощённая модель
        const ohw = (other.w || 20) / 2;
        const ohh = (other.h || 10) / 2;
        const ox = other.x ?? 50, oy = other.y ?? 50;
        xAnchors.push(ox, ox - ohw, ox + ohw);
        yAnchors.push(oy, oy - ohh, oy + ohh);
    }

    // Ищем ближайший snap по X
    for (const my of myXTargets) {
        for (const a of xAnchors) {
            if (Math.abs(my.value - a) < SNAP_THRESHOLD) {
                snapX = x + (a - my.value);
                lines.push({ dir: 'v', pos: a });
                break;
            }
        }
        if (snapX != null) break;
    }
    // Ищем ближайший snap по Y
    for (const my of myYTargets) {
        for (const a of yAnchors) {
            if (Math.abs(my.value - a) < SNAP_THRESHOLD) {
                snapY = y + (a - my.value);
                lines.push({ dir: 'h', pos: a });
                break;
            }
        }
        if (snapY != null) break;
    }

    return { x: snapX, y: snapY, lines };
}

function _anchorFactorX(anchor) {
    if (anchor === 'center') return 0.5;
    if (anchor.endsWith('-left')) return 0;
    if (anchor.endsWith('-right')) return 1;
    return 0.5;
}
function _anchorFactorY(anchor) {
    if (anchor === 'center') return 0.5;
    if (anchor.startsWith('top-')) return 0;
    if (anchor.startsWith('bottom-')) return 1;
    return 0.5;
}

/** Преобразовать описание элемента в CSS-стиль (позиция/размер/фон). */
function elementToStyle(el) {
    const w = `${el.w ?? 20}%`;
    const h = `${el.h ?? 10}%`;
    const anchor = el.anchor || 'center';
    // Phase 6.3.1: AnchorPoint -- точка ВНУТРИ элемента (0..1 по обеим осям),
    // относительно которой считается позиция. Если el.anchorPoint не задан,
    // вычисляем по anchor: center → {0.5, 0.5}, top-left → {0, 0}, и т.д.
    // (это сохраняет старое поведение). Юзер может override через anchorPoint.
    const apDefault = {
        x: anchor === 'right' || anchor.endsWith('-right') ? 1
            : (anchor === 'left' || anchor.endsWith('-left') ? 0 : 0.5),
        y: anchor === 'bottom' || anchor.startsWith('bottom-') ? 1
            : (anchor === 'top' || anchor.startsWith('top-') ? 0 : 0.5),
    };
    const ap = el.anchorPoint && typeof el.anchorPoint === 'object'
        ? {
            x: typeof el.anchorPoint.x === 'number' ? el.anchorPoint.x : apDefault.x,
            y: typeof el.anchorPoint.y === 'number' ? el.anchorPoint.y : apDefault.y,
          }
        : apDefault;
    let left, top, transform;
    // Левый/верх вычисляется по anchor (ссылочная точка на экране).
    // translate(-anchorPoint*100% по каждой оси) -- сдвиг сам элемент так,
    // чтобы anchorPoint оказался в (left, top).
    const tx = -ap.x * 100;
    const ty = -ap.y * 100;
    switch (anchor) {
        case 'top-left':     left = `${el.x ?? 0}%`;          top = `${el.y ?? 0}%`;          break;
        case 'top-right':    left = `${100 - (el.x ?? 0)}%`;  top = `${el.y ?? 0}%`;          break;
        case 'bottom-left':  left = `${el.x ?? 0}%`;          top = `${100 - (el.y ?? 0)}%`;  break;
        case 'bottom-right': left = `${100 - (el.x ?? 0)}%`;  top = `${100 - (el.y ?? 0)}%`;  break;
        // Phase 6.3.1: одиночные стороны -- ссылочная точка на середине стороны.
        case 'top':          left = `${el.x ?? 50}%`;         top = `${el.y ?? 0}%`;          break;
        case 'bottom':       left = `${el.x ?? 50}%`;         top = `${100 - (el.y ?? 0)}%`;  break;
        case 'left':         left = `${el.x ?? 0}%`;          top = `${el.y ?? 50}%`;         break;
        case 'right':        left = `${100 - (el.x ?? 0)}%`;  top = `${el.y ?? 50}%`;         break;
        case 'center':
        default:             left = `${el.x ?? 50}%`;         top = `${el.y ?? 50}%`;         break;
    }
    // Задача 03: rotation + scale через transform. Добавляются ПОСЛЕ translate.
    // hoverScale/activeScale хранятся в el._dynScale (выставляется hover-handler'ом
    // в GuiElement через mutate-ref). При штатном рендере читаем el.scaleX/scaleY.
    const sx = (typeof el._dynScaleX === 'number' ? el._dynScaleX : 1)
        * (typeof el.scaleX === 'number' ? el.scaleX : 1);
    const sy = (typeof el._dynScaleY === 'number' ? el._dynScaleY : 1)
        * (typeof el.scaleY === 'number' ? el.scaleY : 1);
    const rot = (typeof el._dynRotation === 'number' ? el._dynRotation : 0)
        + (typeof el.rotation === 'number' ? el.rotation : 0);
    const brightness = (typeof el._dynBrightness === 'number' ? el._dynBrightness : 1);
    transform = `translate(${tx}%, ${ty}%)`;
    if (sx !== 1 || sy !== 1) transform += ` scale(${sx}, ${sy})`;
    if (rot) transform += ` rotate(${rot}deg)`;
    let bg = el.bgColor || '#1f1810';
    const opacity = el.bgOpacity == null ? 1 : Math.max(0, Math.min(1, el.bgOpacity));
    if (bg === 'transparent' || opacity === 0) bg = 'transparent';
    else bg = hexToRgba(bg, opacity);
    // Задача 03: bgGradient — { stops: ['#a','#b'] | [{c,p}], angle: 0..360 }.
    // Если задан — перебиваем background.
    if (el.bgGradient && Array.isArray(el.bgGradient.stops) && el.bgGradient.stops.length >= 2) {
        const angle = Number.isFinite(el.bgGradient.angle) ? el.bgGradient.angle : 90;
        const parts = el.bgGradient.stops.map((s, i, arr) => {
            if (typeof s === 'string') {
                const p = (i / (arr.length - 1)) * 100;
                return `${s} ${p.toFixed(1)}%`;
            }
            const c = s.c || '#000';
            const p = typeof s.p === 'number' ? s.p * 100 : (i / (arr.length - 1)) * 100;
            return `${c} ${p.toFixed(1)}%`;
        });
        bg = `linear-gradient(${angle}deg, ${parts.join(', ')})`;
    }
    return {
        position: 'absolute',
        left, top, transform,
        transformOrigin: 'center center',
        width: w, height: h,
        background: bg,
        border: el.borderWidth > 0
            ? `${el.borderWidth}px solid ${el.borderColor || '#5a4a3a'}`
            : 'none',
        borderRadius: (el.borderRadius || 0) + 'px',
        boxSizing: 'border-box',
        boxShadow: el.shadow
            ? '0 6px 16px rgba(0,0,0,0.45)'
            : (el.type === 'button' ? '0 2px 6px rgba(0,0,0,0.3)' : 'none'),
        overflow: el.type === 'frame' ? 'hidden' : 'visible',
        filter: brightness !== 1 ? `brightness(${brightness})` : undefined,
    };
}

function hexToRgba(hex, alpha = 1) {
    if (!hex || typeof hex !== 'string') return 'rgba(0,0,0,' + alpha + ')';
    if (hex.startsWith('rgba') || hex.startsWith('rgb')) return hex;
    let h = hex.replace('#', '');
    if (h.length === 3) h = h.split('').map(c => c + c).join('');
    if (h.length !== 6) return hex;
    const r = parseInt(h.slice(0, 2), 16);
    const g = parseInt(h.slice(2, 4), 16);
    const b = parseInt(h.slice(4, 6), 16);
    return `rgba(${r},${g},${b},${alpha})`;
}

export default GuiOverlay;
