/**
 * MinimapOverlay — мини-карта в правом верхнем углу редактора.
 *
 * Показывается, когда включён streaming-режим террейна (большая карта,
 * рендерится только видимая часть). Помогает игроку/пользователю
 * ориентироваться: где он сейчас на карте, куда направлена камера.
 *
 * Реализация:
 *   - canvas 200×200 с top-down рендером сгенерированной карты по
 *     WorldGenerator.sampleHeight/sampleBiome (тот же что в TerrainGenPanel)
 *   - Render-distance круг от текущей позиции камеры/игрока
 *   - Стрелка-направление куда смотрит камера
 *
 * Mini-map обновляется при изменении позиции (раз в 150мс).
 */
import React, { useRef, useEffect, useState } from 'react';
import { WorldGenerator, DEFAULT_GENERATOR_PARAMS } from './engine/voxel/WorldGenerator';

const MINI_SIZE = 200;     // px на canvas
// WORLD_VIEW_M вычисляется динамически по window.__lastGenSize.
// Если генератор не вызывался — fallback 200м.
function getWorldViewM() {
    const size = window.__lastGenSize;
    if (!size) return 200;
    // size в voxel-units, voxelSize=0.25м, и size = halfSize (от центра).
    // Полная ширина = size × 2 × 0.25м.
    return size * 2 * 0.25;
}

const BIOME_COLOR = {
    desert: [220, 200, 120],
    plain: [110, 175, 90],
    forest: [70, 140, 60],
    mountain: [140, 140, 140],
    snow: [240, 245, 250],
    grass: [110, 175, 90],
    sand: [220, 200, 120],
    rock: [140, 140, 140],
};

function biomeColor(biome) {
    return BIOME_COLOR[biome?.id] ?? BIOME_COLOR[biome?.topMaterial] ?? [128, 128, 128];
}

export default function MinimapOverlay({ scene }) {
    const canvasRef = useRef(null);
    const [pos, setPos] = useState({ x: 0, z: 0, yaw: 0 });
    // Размер мира меняется при каждой генерации — храним актуальный.
    const [worldViewM, setWorldViewM] = useState(getWorldViewM());
    // Версия params — инкрементируется когда меняется __lastGenSize/Params,
    // чтобы перерисовать canvas.
    const [paramsVersion, setParamsVersion] = useState(0);
    // visible как internal state. Поллим scene._terrainStreamingEnabled,
    // потому что mutable ref не триггерит React re-render.
    const [visible, setVisible] = useState(false);
    useEffect(() => {
        if (!scene) return;
        const interval = setInterval(() => {
            // 2026-06-02: миникарту показываем ТОЛЬКО если игра её явно включила
            // (scene._minimapEnabled, флаг из настроек сцены). Раньше она
            // появлялась автоматически на любой большой карте (streaming) и
            // мешала — лишний оверлей в играх, где карта не нужна.
            const v = !!scene._minimapEnabled;
            setVisible(prev => prev !== v ? v : prev);
        }, 500);
        return () => clearInterval(interval);
    }, [scene]);

    // Поллинг __lastGenSize / __robloxMinimapGrid: если новая генерация или
    // загрузка проекта их изменила — bump version (перерисовать canvas).
    useEffect(() => {
        if (!visible) return;
        let lastSize = window.__lastGenSize;
        let lastGrid = window.__robloxMinimapGrid;
        const interval = setInterval(() => {
            if (window.__lastGenSize !== lastSize || window.__robloxMinimapGrid !== lastGrid) {
                lastSize = window.__lastGenSize;
                lastGrid = window.__robloxMinimapGrid;
                setParamsVersion(v => v + 1);
            }
        }, 500);
        return () => clearInterval(interval);
    }, [visible]);

    // Render mini-map (heightmap+biomes) — пересобираем когда меняется
    // visible (показали панель) или params (новая генерация).
    // window.__lastGenSize обновляется внутри __voxelGenerate. Сбрасываем
    // canvas при visible-смене.
    useEffect(() => {
        if (!visible) return;
        const canvas = canvasRef.current;
        if (!canvas) return;
        const ctx = canvas.getContext('2d');

        const currentWorldM = getWorldViewM();
        setWorldViewM(currentWorldM);
        const w = MINI_SIZE;
        const h = MINI_SIZE;
        const halfWorld = currentWorldM * 2;   // voxel-units (halfSize)

        // Три режима рендера мини-карты:
        // 1. PROCEDURAL: есть GeneratorParams → рисуем через WorldGenerator
        //    (быстро, идеально точно для свежегенерированного мира)
        // 2. ROBLOX-GRID: гладкий ландшафт (RobloxTerrain) → строим heightmap
        //    из density-grid (window.__robloxMinimapGrid)
        // 3. REAL-DATA: воксельный проект из БД → heightmap из voxel'ов
        //    TerrainManager (top-down проекция)
        const robloxGrid = window.__robloxMinimapGrid;
        const hasGenParams = !!window.__lastGenParams;
        const img = ctx.createImageData(w, h);
        const data = img.data;

        if (robloxGrid) {
            // === ROBLOX-GRID режим — гладкий ландшафт ===
            // density-grid: для каждого столбца (ix,iz) ищем верхнюю solid-ячейку.
            // Цвет = материал этой ячейки, яркость = высота.
            const g = robloxGrid;
            const sx = g.size.x, sy = g.size.y, sz = g.size.z;
            // Цвета материалов гладкого ландшафта.
            const MAT_COLORS = {
                grass: [104, 168, 78], dirt: [120, 84, 52],
                rock: [128, 126, 122], rock_moss: [96, 128, 84],
                sand: [222, 202, 130], snow: [240, 244, 248],
                water: [58, 124, 196], glacier: [198, 220, 240],
                mud: [82, 58, 38],
            };
            // Топ-проекция: для каждого пикселя берём ячейку grid.
            // grid → pixel: ix/sx * w. Высоту нормализуем для яркости.
            const tops = new Int16Array(w * h);     // top cell-Y или -1
            const matIdx = new Uint8Array(w * h);   // mat-id верхней ячейки
            let minTop = 9999, maxTop = -9999;
            for (let py = 0; py < h; py++) {
                const iz = Math.min(sz - 1, (py / h * sz) | 0);
                for (let px = 0; px < w; px++) {
                    const ix = Math.min(sx - 1, (px / w * sx) | 0);
                    let topY = -1, topMat = 0;
                    for (let iy = sy - 1; iy >= 0; iy--) {
                        if (g.isSolid(ix, iy, iz)) {
                            topY = iy;
                            topMat = g.getMatId(ix, iy, iz);
                            break;
                        }
                    }
                    const k = py * w + px;
                    tops[k] = topY;
                    matIdx[k] = topMat;
                    if (topY >= 0) {
                        if (topY < minTop) minTop = topY;
                        if (topY > maxTop) maxTop = topY;
                    }
                }
            }
            const tRange = Math.max(1, maxTop - minTop);
            for (let k = 0; k < w * h; k++) {
                const topY = tops[k];
                let r, gc, b;
                if (topY < 0) {
                    // Пусто над всем столбцом — небесный тон (вода/пропасть)
                    r = 70; gc = 120; b = 175;
                } else {
                    const matKey = g.palette[matIdx[k]] || 'grass';
                    const base = MAT_COLORS[matKey] || [110, 150, 90];
                    const lighten = 0.62 + ((topY - minTop) / tRange) * 0.6;
                    r = Math.min(255, base[0] * lighten | 0);
                    gc = Math.min(255, base[1] * lighten | 0);
                    b = Math.min(255, base[2] * lighten | 0);
                }
                const idx = k * 4;
                data[idx] = r; data[idx + 1] = gc; data[idx + 2] = b; data[idx + 3] = 255;
            }
        } else if (hasGenParams) {
            // === PROCEDURAL режим ===
            const params = window.__lastGenParams;
            const gen = new WorldGenerator(params);
            const step = (currentWorldM * 4) / w;
            const heights = new Float32Array(w * h);
            let minH = Infinity, maxH = -Infinity;
            for (let py = 0; py < h; py++) {
                const wz = -halfWorld + py * step;
                for (let px = 0; px < w; px++) {
                    const wx = -halfWorld + px * step;
                    const ht = gen.sampleHeight(wx, wz);
                    heights[py * w + px] = ht;
                    if (ht < minH) minH = ht;
                    if (ht > maxH) maxH = ht;
                }
            }
            const hRange = Math.max(1, maxH - minH);
            for (let py = 0; py < h; py++) {
                const wz = -halfWorld + py * step;
                for (let px = 0; px < w; px++) {
                    const wx = -halfWorld + px * step;
                    const biome = gen.sampleBiome(wx, wz);
                    const ht = heights[py * w + px];
                    let r, g, b;
                    if (ht <= 0) { r = 60; g = 130; b = 200; }
                    else {
                        const base = biomeColor(biome);
                        const lighten = 0.7 + ((ht - minH) / hRange) * 0.6;
                        r = Math.min(255, base[0] * lighten | 0);
                        g = Math.min(255, base[1] * lighten | 0);
                        b = Math.min(255, base[2] * lighten | 0);
                    }
                    const idx = (py * w + px) * 4;
                    data[idx] = r; data[idx + 1] = g; data[idx + 2] = b; data[idx + 3] = 255;
                }
            }
        } else {
            // === REAL-DATA режим — для загруженных проектов ===
            // Строим heightmap из voxels TerrainManager:
            // для каждой (x,z) находим max Y где есть voxel.
            const tm = scene?.terrainManager;
            const voxels = tm?.voxels;
            if (!voxels || voxels.size === 0) {
                // Fallback на тёмно-серый
                for (let i = 0; i < w * h * 4; i += 4) {
                    data[i] = 30; data[i + 1] = 32; data[i + 2] = 40; data[i + 3] = 255;
                }
            } else {
                // Топ-проекция: для каждого pixel = (worldX, worldZ),
                // находим max Y в подходящих voxel-bucket'ах.
                // Аггрегируем сразу по всем voxel'ам — это быстрее чем
                // sampling per-pixel при огромном количестве voxel'ов.
                const buckets = new Map();   // "px,py" → { maxY, mat }
                const pixelsPerVoxel = w / (halfWorld * 2);
                for (const [key, mat] of voxels) {
                    const lastComma = key.lastIndexOf(',');
                    const midComma = key.lastIndexOf(',', lastComma - 1);
                    const x = parseInt(key.slice(0, midComma), 10);
                    const y = parseInt(key.slice(midComma + 1, lastComma), 10);
                    const z = parseInt(key.slice(lastComma + 1), 10);
                    // Перевод voxel-coords в pixel-coords
                    const px = Math.floor((x + halfWorld) * pixelsPerVoxel);
                    const py = Math.floor((z + halfWorld) * pixelsPerVoxel);
                    if (px < 0 || px >= w || py < 0 || py >= h) continue;
                    const bk = py * w + px;
                    const ex = buckets.get(bk);
                    if (!ex || y > ex.y) {
                        buckets.set(bk, { y, mat });
                    }
                }

                // Цвета материалов для real-data рендера
                const MAT_COLORS = {
                    grass: [110, 175, 90], dirt: [120, 80, 50],
                    rock: [130, 130, 135], rock_moss: [90, 130, 80],
                    sand: [220, 200, 120], snow: [240, 245, 250],
                    water: [60, 130, 200], glacier: [200, 220, 240],
                    wood: [120, 80, 40], trunk: [110, 70, 40], trunk_white: [220, 220, 215],
                    leaves: [55, 130, 55], leaves_orange: [200, 110, 40],
                    mud: [80, 55, 35], asphalt: [60, 60, 65], concrete: [180, 180, 180],
                    salt: [240, 240, 245],
                    flower_red: [200, 60, 60], flower_blue: [70, 110, 200], flower_yellow: [220, 200, 70],
                    mushroom_red: [180, 50, 50], tall_grass: [100, 160, 80],
                };
                // Найти min/max Y для нормализации яркости
                let minY = Infinity, maxY = -Infinity;
                for (const v of buckets.values()) {
                    if (v.y < minY) minY = v.y;
                    if (v.y > maxY) maxY = v.y;
                }
                const yRange = Math.max(1, maxY - minY);
                // Заливаем все пиксели
                for (let i = 0; i < w * h; i++) {
                    const v = buckets.get(i);
                    let r, g, b;
                    if (!v) {
                        // Пусто — небесный тон
                        r = 130; g = 160; b = 200;
                    } else {
                        const base = MAT_COLORS[v.mat] || [128, 128, 128];
                        const lighten = 0.65 + ((v.y - minY) / yRange) * 0.55;
                        r = Math.min(255, base[0] * lighten | 0);
                        g = Math.min(255, base[1] * lighten | 0);
                        b = Math.min(255, base[2] * lighten | 0);
                    }
                    const idx = i * 4;
                    data[idx] = r; data[idx + 1] = g; data[idx + 2] = b; data[idx + 3] = 255;
                }
            }
        }
        ctx.putImageData(img, 0, 0);
    }, [visible, paramsVersion, scene]);

    // Отслеживаем позицию игрока/камеры
    useEffect(() => {
        if (!visible || !scene) return;
        const interval = setInterval(() => {
            let x = 0, z = 0, yaw = 0;
            if (scene._isPlaying && scene.player && scene.player._pos) {
                x = scene.player._pos.x;
                z = scene.player._pos.z;
                yaw = scene.player._yaw ?? 0;
            } else if (scene.camera && scene.camera.position) {
                x = scene.camera.position.x;
                z = scene.camera.position.z;
                // Yaw камеры — alpha в ArcRotateCamera или rotation.y в FreeCamera
                yaw = scene.camera.alpha ?? scene.camera.rotation?.y ?? 0;
            }
            setPos({ x, z, yaw });
        }, 150);
        return () => clearInterval(interval);
    }, [visible, scene]);

    if (!visible) return null;

    // Конвертация мировых координат → pixel canvas
    // World (-worldViewM/2 .. +worldViewM/2) → (0 .. MINI_SIZE)
    const halfWorldM = worldViewM / 2;
    const worldToPixel = (wx, wz) => {
        const px = ((wx + halfWorldM) / worldViewM) * MINI_SIZE;
        const py = ((wz + halfWorldM) / worldViewM) * MINI_SIZE;
        // Clamp на случай если игрок ушёл за пределы карты
        return {
            px: Math.max(0, Math.min(MINI_SIZE, px)),
            py: Math.max(0, Math.min(MINI_SIZE, py)),
            outOfBounds: px < 0 || px > MINI_SIZE || py < 0 || py > MINI_SIZE,
        };
    };
    const player = worldToPixel(pos.x, pos.z);
    // Render distance круг — 60м (масштабируется относительно worldViewM)
    const renderRadiusPx = (60 / worldViewM) * MINI_SIZE;

    return (
        <div style={{
            position: 'absolute',
            top: 12,
            right: 12,
            width: MINI_SIZE + 10,
            background: 'rgba(20, 22, 30, 0.85)',
            border: '1px solid rgba(255, 255, 255, 0.15)',
            borderRadius: 8,
            padding: 5,
            zIndex: 100,
            pointerEvents: 'none',
            boxShadow: '0 4px 16px rgba(0,0,0,0.4)',
        }}>
            <div style={{ position: 'relative', width: MINI_SIZE, height: MINI_SIZE }}>
                <canvas
                    ref={canvasRef}
                    width={MINI_SIZE}
                    height={MINI_SIZE}
                    style={{
                        width: MINI_SIZE,
                        height: MINI_SIZE,
                        imageRendering: 'pixelated',
                        borderRadius: 4,
                        display: 'block',
                    }}
                />
                {/* Render distance круг (полупрозрачный) */}
                <div style={{
                    position: 'absolute',
                    left: player.px - renderRadiusPx,
                    top: player.py - renderRadiusPx,
                    width: renderRadiusPx * 2,
                    height: renderRadiusPx * 2,
                    border: '1.5px solid rgba(255, 255, 0, 0.4)',
                    borderRadius: '50%',
                    boxSizing: 'border-box',
                }} />
                {/* Точка игрока */}
                <div style={{
                    position: 'absolute',
                    left: player.px - 4,
                    top: player.py - 4,
                    width: 8,
                    height: 8,
                    background: '#ff4444',
                    border: '2px solid white',
                    borderRadius: '50%',
                    boxSizing: 'border-box',
                }} />
            </div>
            <div style={{
                marginTop: 4,
                fontSize: 10,
                color: 'rgba(255,255,255,0.7)',
                textAlign: 'center',
                fontFamily: 'monospace',
            }}>
                X:{pos.x.toFixed(0)} Z:{pos.z.toFixed(0)} · карта {worldViewM.toFixed(0)}м
                {player.outOfBounds && <span style={{ color: '#ff6b6b' }}> · ВНЕ КАРТЫ</span>}
            </div>
        </div>
    );
}
