import './style.css';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// import { io } from "socket.io-client";
import { turnManager, PHASES } from './systems/TurnManager.js';
import { dungeonDeck } from './dungeon/DungeonDecks.js';
import * as TileDefinitions from './dungeon/TileDefinitions.js';
import { eventDeck } from './dungeon/EventDeck.js'; // Import Event Deck
import { TILE_DEFINITIONS, getTileDefinition } from './dungeon/TileDefinitions.js';

// --- NETWORK SETUP ---
// Dynamic connection to support playing from mobile on the same network
const socketUrl = `http://${window.location.hostname}:3001`;
// const socket = io(socketUrl); 
// MOCK SOCKET to avoid connection errors while debugging
const socket = {
    on: (event, callback) => console.log(`[MockSocket] Listening for ${event}`),
    emit: (event, data) => console.log(`[MockSocket] Emitting ${event}`, data),
    id: 'mock-id'
};
let lobbyCode = null;

socket.on("connect", () => {
    console.log("Connected to Game Server!", socket.id);
    socket.emit("HOST_GAME");
});

socket.on("LOBBY_CREATED", ({ code }) => {
    console.log("GAME HOSTED. LOBBY CODE:", code);
    lobbyCode = code;
    // Temporary UI for Lobby Code
    const codeDisplay = document.createElement('div');
    codeDisplay.style.position = 'absolute';
    codeDisplay.style.top = '10px';
    codeDisplay.style.right = '10px';
    codeDisplay.style.color = 'gold';
    codeDisplay.style.fontFamily = 'monospace';
    codeDisplay.style.fontSize = '24px';
    codeDisplay.style.fontWeight = 'bold';
    codeDisplay.style.background = 'rgba(0,0,0,0.5)';
    codeDisplay.style.padding = '10px';
    codeDisplay.innerText = `SALA: ${code}`;
    document.body.appendChild(codeDisplay);
});

socket.on("PLAYER_JOINED", ({ name }) => {
    console.log(`Player ${name} has joined!`);
    // TODO: Show notification
});

socket.on("PLAYER_ACTION", ({ playerId, action, data }) => {
    console.log(`Action received from ${playerId}: ${action}`, data);
    // Placeholder interaction
    if (action === 'MOVE') {
        // Example: data = { x: 1, y: 0 }
        // Implement logic here
    }
});

// --- CONFIGURACIÓN DE LA ESCENA ---
const CONFIG = {
    CELL_SIZE: 2,           // Unidades de Three.js por celda lógica
    TILE_DIMENSIONS: 4,     // Una tile es de 4x4 celdas
};

// --- ESTADO DEL JUEGO (DATA MODEL) ---
const ASSETS = {
    tiles: {
        'tile_base': { src: '/assets/images/tiles/tile4x4.png', width: 4, height: 4 },
        'tile_8x4': { src: '/assets/images/tiles/tile4x4.png', width: 8, height: 4 },
        'tile_4x8': { src: '/assets/images/tiles/tile4x4.png', width: 4, height: 8 },
        'tile_8x8': { src: '/assets/images/tiles/tile4x4.png', width: 8, height: 8 },
        'tile_cyan': { src: '/assets/images/tiles/tile4x4_blue.png', width: 4, height: 4 },
        'tile_orange': { src: '/assets/images/tiles/tile4x4_orange.png', width: 4, height: 4 },
        'wall_1': { src: '/assets/images/tiles/pared1.png' },
        'door_1': { src: '/assets/images/tiles/puerta1.png' },
    },
    standees: {
        'hero_1': { src: '/assets/images/standees/barbaro.png', height: 3 },
        'hero_2': { src: '/assets/images/standees/esqueleto.png', height: 3 },
    }
};

// Sistema de salas
// --- GENERADOR PROCEDURAL DE MAZMORRAS ---
// --- GENERACIÓN DE MAZMORRA (DINÁMICA) ---
function generateDungeon() {
    // Start with a single entry room 4x4
    const startTileDef = getTileDefinition('room_4x4_normal');
    const startRoom = {
        id: 1,
        tileDef: startTileDef, // Store the full tile definition
        tile: { id: startTileDef.id, x: 0, y: 0 },
        walls: [], // Walls calculated dynamically later or fixed for start
        doors: [
            { side: 'N', gridX: 2, gridY: 0, leadsTo: null, id: 'door_start_N', isOpen: false }
        ],
        entities: [
            { id: 101, type: 'hero_1', x: 2, y: 2 }
        ]
    };

    return {
        rooms: [startRoom],
        visitedRooms: new Set([1]),
        currentRoom: 1
    };
}

// Helper function to create a flipped version of a tile
function createFlippedTile(tileDef, direction) {
    const flipped = { ...tileDef };

    // Create a new flipped walkability matrix
    const { walkability, width, height } = tileDef;
    const newWalkability = [];

    if (direction === 'horizontal') {
        // Flip horizontally (mirror left-right)
        for (let y = 0; y < height; y++) {
            newWalkability[y] = [...walkability[y]].reverse();
        }
        flipped.id = tileDef.id + '_flipped_h';
    } else {
        // Flip vertically (mirror top-bottom)
        for (let y = 0; y < height; y++) {
            newWalkability[y] = walkability[height - 1 - y];
        }
        flipped.id = tileDef.id + '_flipped_v';
    }

    flipped.walkability = newWalkability;
    // Note: We keep the same image, the flip is only for walkability logic
    // If you want visual flipping, you'd need to modify the rendering code

    return flipped;
}

// --- EXPLORACIÓN DINÁMICA ---
function exploreRoom(originRoom, door) {
    // Draw an event card
    const eventCard = eventDeck.drawCard();
    if (eventCard) {
        console.log("Event Card Drawn:", eventCard);
        showUIEvent(eventCard);
    }

    // Determine entry side (opposite of exit)
    let entrySide;
    if (door.side === 'N') entrySide = 'S';
    else if (door.side === 'S') entrySide = 'N';
    else if (door.side === 'E') entrySide = 'W';
    else if (door.side === 'W') entrySide = 'E';

    // Try to draw a compatible abstract tile type (up to 10 attempts)
    let card = null;
    let placementOffset = 0; // Offset for L-shapes based on selection
    let attempts = 0;
    const maxAttempts = 10;

    while (attempts < maxAttempts && !card) {
        const abstractCard = dungeonDeck.drawCompatibleCard(originRoom.tileDef.tileType);
        if (!abstractCard) {
            console.warn("Could not draw compatible card");
            return null;
        }

        console.log(`Drew abstract type: ${abstractCard.type}`);

        // Select concrete tile variant based on abstract type
        let candidates = [];

        switch (abstractCard.type) {
            case 'room_4x4':
                candidates = TileDefinitions.ROOMS.filter(r => r.width === 4 && r.height === 4);
                break;
            case 'room_4x6':
                candidates = TileDefinitions.ROOMS.filter(r => r.width === 4 && r.height === 6);
                break;
            case 'corridor':
                // Filter by orientation (EW or NS based on exit direction)
                const isExitHorizontal = door.side === 'E' || door.side === 'W';
                candidates = TileDefinitions.CORRIDORS.filter(c => {
                    const isCorridorHorizontal = c.exits.includes('E') && c.exits.includes('W');
                    return isExitHorizontal === isCorridorHorizontal;
                });
                break;
            case 'L':
                // NEW RULE: Select L-shape based on corridor exit direction
                if (door.side === 'N') {
                    // North exit (vertical corridor going up): choose LSE or LSW
                    const lOptions = [
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_SE, offset: -1 },  // LSE with offset -1
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_WS, offset: 1 }    // LSW with offset +1
                    ];
                    const selected = lOptions[Math.floor(Math.random() * lOptions.length)];
                    card = selected.tile;
                    placementOffset = selected.offset;
                    console.log(`✓ Selected ${card.id} for N exit with offset ${placementOffset}`);
                } else if (door.side === 'S') {
                    // South exit (vertical corridor going down): choose LNE or LNW
                    const lOptions = [
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_NE, offset: 1 },   // LNE with offset 0
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_WN, offset: -1 }    // LNW with offset +2
                    ];
                    const selected = lOptions[Math.floor(Math.random() * lOptions.length)];
                    card = selected.tile;
                    placementOffset = selected.offset;
                    console.log(`✓ Selected ${card.id} for S exit with offset ${placementOffset}`);
                } else if (door.side === 'E') {
                    // East exit: choose LWN or LWS
                    const lOptions = [
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_WN, offset: 1 },   // LWN with offset 0
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_WS, offset: -1 }    // LWS with offset +2
                    ];
                    const selected = lOptions[Math.floor(Math.random() * lOptions.length)];
                    card = selected.tile;
                    placementOffset = selected.offset;
                    console.log(`✓ Selected ${card.id} for E exit with offset ${placementOffset}`);
                } else if (door.side === 'W') {
                    // West exit: choose LNE or LSE
                    const lOptions = [
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_NE, offset: 1 },   // LNE with offset +2
                        { tile: TileDefinitions.TILE_DEFINITIONS.L_SE, offset: -1 }    // LSE with offset 0
                    ];
                    const selected = lOptions[Math.floor(Math.random() * lOptions.length)];
                    card = selected.tile;
                    placementOffset = selected.offset;
                    console.log(`✓ Selected ${card.id} for W exit with offset ${placementOffset}`);
                }
                break;
            case 'T':
                // T-junction placement based on corridor exit direction
                if (door.side === 'N') {
                    // North exit: choose T_NES, T_WNS, or T_WSE
                    const tOptions = [
                        { tile: TileDefinitions.TILE_DEFINITIONS.T_NES, offset: -1 },  // T_NES with offset -1
                        { tile: TileDefinitions.TILE_DEFINITIONS.T_WNS, offset: 1 },   // T_WNS with offset +1
                        { tile: TileDefinitions.TILE_DEFINITIONS.T_WSE, offset: 0 }    // T_WSE with offset 0 (placeholder)
                    ];
                    const selected = tOptions[Math.floor(Math.random() * tOptions.length)];
                    card = selected.tile;
                    placementOffset = selected.offset;
                    console.log(`✓ Selected ${card.id} for N exit with offset ${placementOffset}`);
                } else {
                    // For other exits, use all T-junctions (fallback)
                    candidates = [...TileDefinitions.T_JUNCTIONS];
                }
                break;
        }

        // If we already selected a specific card (L-shapes with N/S exits), skip validation
        if (card) {
            break;
        }

        if (candidates.length === 0) {
            console.warn(`No candidates found for type ${abstractCard.type}`);
            attempts++;
            continue;
        }

        // For rooms: randomly select one and optionally flip it
        if (abstractCard.type === 'room_4x4' || abstractCard.type === 'room_4x6') {
            const selectedRoom = candidates[Math.floor(Math.random() * candidates.length)];

            // Randomly decide to flip horizontally or vertically
            const shouldFlip = Math.random() < 0.5;
            const flipDirection = Math.random() < 0.5 ? 'horizontal' : 'vertical';

            if (shouldFlip) {
                card = createFlippedTile(selectedRoom, flipDirection);
                console.log(`✓ Selected ${selectedRoom.id} and flipped ${flipDirection}`);
            } else {
                card = selectedRoom;
                console.log(`✓ Selected ${selectedRoom.id} without flipping`);
            }
            break;
        }

        // For corridors and T-junctions: validate and select
        const fittingVariants = [];
        for (const variant of candidates) {
            const connectionResult = canConnectTiles(originRoom, variant, door.side);
            if (connectionResult.valid) {
                fittingVariants.push(variant);
            }
        }

        if (fittingVariants.length > 0) {
            // RANDOM selection from fitting variants
            card = fittingVariants[Math.floor(Math.random() * fittingVariants.length)];
            console.log(`✓ Selected ${card.id} (${card.tileType}) randomly from ${fittingVariants.length} fitting variants`);
        } else {
            console.log(`✗ No ${abstractCard.type} variant fits, trying another tile type...`);
            attempts++;
        }
    }

    if (!card) {
        console.error("Could not find valid tile after", maxAttempts, "attempts");
        return null;
    }

    const nextTileDef = card;
    const newRoomId = ROOMS.rooms.length + 1;

    // Calculate entry door position relative to the new tile origin
    // We use external adjacent coordinates (-1 or width/height) to place the new tile 
    // strictly adjacent to the previous one, preventing 1-cell overlaps.
    let entryGridX, entryGridY;

    // Use explicit door coordinates if available (for asymmetric tiles like L/T)
    const doorCoords = nextTileDef.doorCoordinates ? nextTileDef.doorCoordinates[entrySide] : null;

    if (entrySide === 'N') {
        entryGridX = doorCoords ? doorCoords.x : Math.floor(nextTileDef.width / 2);
        entryGridY = -1;
    } else if (entrySide === 'S') {
        entryGridX = doorCoords ? doorCoords.x : Math.floor(nextTileDef.width / 2);
        entryGridY = nextTileDef.height;
    } else if (entrySide === 'E') {
        entryGridX = nextTileDef.width;
        entryGridY = doorCoords ? doorCoords.y : Math.floor(nextTileDef.height / 2);
    } else if (entrySide === 'W') {
        entryGridX = -1;
        entryGridY = doorCoords ? doorCoords.y : Math.floor(nextTileDef.height / 2);
    }

    // Calculate absolute position for the new tile
    let newX = originRoom.tile.x + door.gridX - entryGridX;
    let newY = originRoom.tile.y + door.gridY - entryGridY;

    // Manual placementOffset removed: Handled by explicit doorCoordinates via TileDefinitions.js

    // Check for collisions
    if (!isAreaFree(newX, newY, nextTileDef.width, nextTileDef.height)) {
        console.warn("Cannot place room: Collision detected!");
        return null;
    }

    const newRoom = {
        id: newRoomId,
        tileDef: nextTileDef,
        tile: { id: nextTileDef.id, x: newX, y: newY },
        walls: ['N', 'S', 'E', 'W'],
        doors: [],
        entities: []
    };

    // Determine if we should place a door or open connection
    const placeDoor = shouldPlaceDoor(originRoom.tileDef.tileType, nextTileDef.tileType);

    // Create the entry door/connection
    const entryDoor = {
        side: entrySide,
        leadsTo: originRoom.id,
        isOpen: !placeDoor, // Open if no door, closed if door
        isDoor: placeDoor,
        id: `door_${newRoomId}_to_${originRoom.id}`,
        gridX: entryGridX,
        gridY: entryGridY
    };

    newRoom.doors.push(entryDoor);

    // Generate additional exits based on the tile definition
    nextTileDef.exits.forEach(exitDir => {
        if (exitDir === entrySide) return; // Already have this connection

        // Check if this is an L or T tile to make exit open by default
        // For L and T tiles, exits are open passages, not closed doors
        const isOpenPassage = (nextTileDef.tileType === 'L' || nextTileDef.tileType === 'T');

        const exitDoor = {
            side: exitDir,
            leadsTo: null,
            isOpen: isOpenPassage, // Open by default for L/T
            isDoor: !isOpenPassage, // Not a blocking door for L/T
            id: `door_${newRoomId}_${exitDir}`
        };

        // Calculate door coordinates using explicit definition or default centering
        if (nextTileDef.doorCoordinates && nextTileDef.doorCoordinates[exitDir]) {
            exitDoor.gridX = nextTileDef.doorCoordinates[exitDir].x;
            exitDoor.gridY = nextTileDef.doorCoordinates[exitDir].y;
        } else {
            // Default centering logic
            if (exitDir === 'N') {
                exitDoor.gridX = Math.floor(nextTileDef.width / 2);
                exitDoor.gridY = 0;
            } else if (exitDir === 'S') {
                exitDoor.gridX = Math.floor(nextTileDef.width / 2);
                exitDoor.gridY = nextTileDef.height - 1;
            } else if (exitDir === 'E') {
                exitDoor.gridX = nextTileDef.width - 1;
                exitDoor.gridY = Math.floor(nextTileDef.height / 2);
            } else if (exitDir === 'W') {
                exitDoor.gridX = 0;
                exitDoor.gridY = Math.floor(nextTileDef.height / 2);
            }
        }

        newRoom.doors.push(exitDoor);
    });

    ROOMS.rooms.push(newRoom);
    console.log(`✓ Tile ${newRoomId} (${nextTileDef.tileType}) created: ${nextTileDef.id} at (${newX}, ${newY})`);
    return newRoom;
}

const ROOMS = generateDungeon();

// --- TURN SYSTEM UI ---
const phaseDisplay = document.createElement('div');
phaseDisplay.style.position = 'absolute';
phaseDisplay.style.top = '10px';
phaseDisplay.style.left = '50%';
phaseDisplay.style.transform = 'translateX(-50%)';
phaseDisplay.style.color = '#fff';
phaseDisplay.style.fontSize = '24px';
phaseDisplay.style.fontWeight = 'bold';
phaseDisplay.style.textShadow = '0 0 5px #000';
phaseDisplay.style.pointerEvents = 'none';
document.body.appendChild(phaseDisplay);

function showUIEvent(event) {
    const toast = document.createElement('div');
    toast.style.position = 'absolute';
    toast.style.top = '20%';
    toast.style.left = '50%';
    toast.style.transform = 'translateX(-50%)';
    toast.style.background = 'rgba(0, 0, 0, 0.8)';
    toast.style.color = '#ffcc00';
    toast.style.padding = '20px';
    toast.style.border = '2px solid #ffcc00';
    toast.style.borderRadius = '10px';
    toast.style.textAlign = 'center';
    toast.style.zIndex = '1000';

    toast.innerHTML = `
        <h2 style="margin:0">${event.title}</h2>
        <p style="margin:10px 0">${event.description}</p>
        <small>${event.type}</small>
    `;

    document.body.appendChild(toast);

    setTimeout(() => {
        toast.remove();
    }, 4000);
}

const PHASE_TRANSLATIONS = {
    'POWER': 'PODER',
    'HERO': 'HÉROES',
    'EXPLORATION': 'EXPLORACIÓN',
    'MONSTER': 'MONSTRUOS',
    'END': 'FIN'
};

turnManager.addEventListener('phaseChange', (e) => {
    const phaseName = PHASE_TRANSLATIONS[e.detail] || e.detail;
    phaseDisplay.innerText = `FASE: ${phaseName}`;

    // Example: Update lighting or UI based on phase
    if (e.detail === PHASES.EXPLORATION) {
        console.log("Entering Exploration Mode - Waiting for door interaction...");
    }
});

turnManager.addEventListener('message', (e) => {
    // Show smaller toast for messages
    const msg = document.createElement('div');
    msg.style.position = 'absolute';
    msg.style.bottom = '100px';
    msg.style.left = '50%';
    msg.style.transform = 'translateX(-50%)';
    msg.style.color = '#fff';
    msg.style.textShadow = '0 0 2px #000';
    msg.innerText = e.detail;
    document.body.appendChild(msg);
    setTimeout(() => msg.remove(), 2000);
});

turnManager.addEventListener('eventTriggered', (e) => {
    showUIEvent(e.detail.event);
});

// Start the game loop
turnManager.startTurn();

const SESSION = {
    selectedUnitId: null,
    selectedDoorId: null, // Nuevo: ID de la puerta seleccionada
    path: [],
    pathMeshes: [],
    roomMeshes: {},
    isAnimating: false,
    textureCache: {},
    currentView: 'N'
};

// --- CONFIGURACIÓN BÁSICA THREE.JS ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x202020);

// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.querySelector('#app').appendChild(renderer.domElement);

// Cámara isométrica (zoom más cercano)
const aspect = window.innerWidth / window.innerHeight;
const d = 8;
const camera = new THREE.OrthographicCamera(-d * aspect, d * aspect, d, -d, 1, 1000);

// Vistas isométricas COMPLETAMENTE predefinidas (sin acumulación de errores)
// Cada vista tiene posición, target Y quaternion fijo
const CAMERA_VIEWS = {
    N: {
        position: new THREE.Vector3(20, 20, 20),
        target: new THREE.Vector3(0, 0, 0),
        up: new THREE.Vector3(0, 1, 0)
    },
    S: {
        position: new THREE.Vector3(-20, 20, -20),
        target: new THREE.Vector3(0, 0, 0),
        up: new THREE.Vector3(0, 1, 0)
    },
    E: {
        position: new THREE.Vector3(-20, 20, 20),
        target: new THREE.Vector3(0, 0, 0),
        up: new THREE.Vector3(0, 1, 0)
    },
    W: {
        position: new THREE.Vector3(20, 20, -20),
        target: new THREE.Vector3(0, 0, 0),
        up: new THREE.Vector3(0, 1, 0)
    }
};

// Precalcular quaternions para cada vista (FIJOS, nunca cambian)
Object.keys(CAMERA_VIEWS).forEach(key => {
    const view = CAMERA_VIEWS[key];
    const tempCamera = new THREE.PerspectiveCamera();
    tempCamera.position.copy(view.position);
    tempCamera.up.copy(view.up);
    tempCamera.lookAt(view.target);
    view.quaternion = tempCamera.quaternion.clone();
});

// OrbitControls solo para zoom y paneo (sin rotación)
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableRotate = false;
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = true;
controls.mouseButtons = {
    LEFT: null,
    MIDDLE: THREE.MOUSE.PAN,
    RIGHT: THREE.MOUSE.PAN
};
controls.zoomToCursor = true;
controls.minZoom = 0.5;
controls.maxZoom = 3;

// Determinar opacidad de pared según vista actual
function getWallOpacity(wallSide, viewDirection) {
    const opacityRules = {
        N: { opaque: ['N', 'W'], transparent: ['S', 'E'] },
        S: { opaque: ['S', 'E'], transparent: ['N', 'W'] },
        E: { opaque: ['N', 'E'], transparent: ['S', 'W'] },
        W: { opaque: ['W', 'S'], transparent: ['N', 'E'] }
    };

    const rule = opacityRules[viewDirection];
    if (rule.opaque.includes(wallSide)) {
        return 1.0; // Opaco
    } else {
        return 0.5; // Semi-transparente
    }
}

// Actualizar opacidades de todas las paredes según la vista actual
function updateWallOpacities() {
    Object.values(SESSION.roomMeshes).forEach(roomData => {
        if (roomData.walls) {
            roomData.walls.forEach(wall => {
                const wallSide = wall.userData.wallSide;
                if (wallSide) {
                    const newOpacity = getWallOpacity(wallSide, SESSION.currentView);
                    wall.material.opacity = newOpacity;
                    wall.material.transparent = newOpacity < 1.0;
                }
            });
        }
    });
}


function setCameraView(direction, animate = true) {
    const view = CAMERA_VIEWS[direction];

    // Encontrar el personaje del jugador para centrar la vista
    let playerPosition = new THREE.Vector3(0, 0, 0);
    for (const room of ROOMS.rooms) {
        const player = room.entities.find(e => e.type === 'hero_1');
        if (player && player.mesh) {
            playerPosition.copy(player.mesh.position);
            playerPosition.y = 0;
            break;
        }
    }

    // Calcular offset de la vista (diferencia entre posición y target definidos en CAMERA_VIEWS)
    const viewOffset = view.position.clone().sub(view.target);

    // Nueva posición de cámara centrada en el jugador
    const targetPosition = playerPosition.clone().add(viewOffset);
    const targetLookAt = playerPosition.clone();

    if (animate && SESSION.currentView !== direction) {
        const startPosition = camera.position.clone();
        const startLookAt = controls.target.clone();

        const duration = 600; // ms
        const startTime = Date.now();

        const animateTransition = () => {
            const elapsed = Date.now() - startTime;
            const progress = Math.min(elapsed / duration, 1);

            // Easing suave
            const eased = progress < 0.5
                ? 2 * progress * progress
                : 1 - Math.pow(-2 * progress + 2, 2) / 2;

            // Interpolación LINEAL de posición y target
            const currentPos = new THREE.Vector3().lerpVectors(startPosition, targetPosition, eased);
            const currentLookAt = new THREE.Vector3().lerpVectors(startLookAt, targetLookAt, eased);

            camera.position.copy(currentPos);
            camera.up.set(0, 1, 0); // FORZAR UP VECTOR SIEMPRE
            camera.lookAt(currentLookAt);

            controls.target.copy(currentLookAt);
            controls.update();

            if (progress < 1) {
                requestAnimationFrame(animateTransition);
            } else {
                // Asegurar estado final perfecto
                camera.position.copy(targetPosition);
                camera.up.set(0, 1, 0);
                camera.lookAt(targetLookAt);
                controls.target.copy(targetLookAt);
                controls.update();
            }
        };

        animateTransition();
    } else {
        // Cambio inmediato
        camera.position.copy(targetPosition);
        camera.up.set(0, 1, 0); // FORZAR UP VECTOR
        camera.lookAt(targetLookAt);
        controls.target.copy(targetLookAt);
        controls.update();
    }

    SESSION.currentView = direction;
    updateCompassUI();
    updateWallOpacities();
}

// Establecer vista inicial
setCameraView('N');

// Luces
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);

const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(10, 20, 5);
dirLight.castShadow = true;
scene.add(dirLight);

const gridHelper = new THREE.GridHelper(40, 40, 0x444444, 0x111111);
scene.add(gridHelper);

// Plano invisible para Raycasting
const planeGeometry = new THREE.PlaneGeometry(200, 200);
const planeMaterial = new THREE.MeshBasicMaterial({ visible: false });
const raycastPlane = new THREE.Mesh(planeGeometry, planeMaterial);
raycastPlane.rotation.x = -Math.PI / 2;
scene.add(raycastPlane);

// --- HELPERS LÓGICOS ---
function worldToGrid(x, z) {
    return {
        x: Math.floor(x / CONFIG.CELL_SIZE),
        y: Math.floor(z / CONFIG.CELL_SIZE)
    };
}

function gridToWorld(gridX, gridY) {
    return {
        x: (gridX * CONFIG.CELL_SIZE) + (CONFIG.CELL_SIZE / 2),
        z: (gridY * CONFIG.CELL_SIZE) + (CONFIG.CELL_SIZE / 2)
    };
}

function isAdjacent(p1, p2) {
    const dx = Math.abs(p1.x - p2.x);
    const dy = Math.abs(p1.y - p2.y);
    return (dx === 1 && dy === 0) || (dx === 0 && dy === 1);
}

// Verificar si una posición está dentro de una sala
function isPositionInRoom(x, y, room) {
    const tile = room.tile;
    const tileDef = room.tileDef;
    if (!tileDef) return false;

    const minX = tile.x;
    const maxX = tile.x + tileDef.width - 1;
    const minY = tile.y;
    const maxY = tile.y + tileDef.height - 1;

    return x >= minX && x <= maxX && y >= minY && y <= maxY;
}

// Verificar colisión entre un rectángulo propuesto y las salas existentes
// x, y: Coordenadas Grid (Top-Left)
// width, height: Dimensiones Grid
function isAreaFree(x, y, width, height) {
    const aMinX = x;
    const aMaxX = x + width;
    const aMinY = y;
    const aMaxY = y + height;

    for (const room of ROOMS.rooms) {
        const tileDef = room.tileDef;
        if (!tileDef) continue;

        // Rectángulo B (Existente)
        const bMinX = room.tile.x;
        const bMaxX = room.tile.x + tileDef.width;
        const bMinY = room.tile.y;
        const bMaxY = room.tile.y + tileDef.height;

        // Check Overlap (Intersección de AABB)
        const noOverlap = aMaxX <= bMinX || aMinX >= bMaxX || aMaxY <= bMinY || aMinY >= bMaxY;

        if (!noOverlap) {
            console.log(`Collision detected with Room ${room.id} [${bMinX},${bMinY},${bMaxX},${bMaxY}] vs New [${aMinX},${aMinY},${aMaxX},${aMaxY}]`);
            return false;
        }
    }
    return true;
}

// Verificar si una posición es una puerta
function isPositionDoor(x, y, room) {
    for (const door of room.doors) {
        const doorPos = getDoorGridPosition(room, door);
        if (doorPos.x === x && doorPos.y === y) {
            return true;
        }
    }
    return false;
}



// Verificar si una celda es transitable usando la matriz de walkability
function isWalkable(x, y) {
    for (const roomId of ROOMS.visitedRooms) {
        const room = ROOMS.rooms.find(r => r.id === roomId);
        if (!room || !room.tileDef) continue;

        if (isPositionInRoom(x, y, room)) {
            // Get local coordinates within the tile
            const localX = x - room.tile.x;
            const localY = y - room.tile.y;

            // Check walkability matrix
            const walkValue = room.tileDef.walkability[localY][localX];
            return walkValue > 0; // 0 = not walkable, >0 = walkable
        }

        // Verificar puertas
        for (const door of room.doors) {
            const doorPos = getDoorGridPosition(room, door);
            if (doorPos.x === x && doorPos.y === y) {
                return door.isOpen; // Solo transitable si está abierta
            }
        }
    }
    return false;
}

// Get the layer/height of a specific position
function getTileLayer(x, y) {
    for (const roomId of ROOMS.visitedRooms) {
        const room = ROOMS.rooms.find(r => r.id === roomId);
        if (!room || !room.tileDef) continue;

        if (isPositionInRoom(x, y, room)) {
            const localX = x - room.tile.x;
            const localY = y - room.tile.y;

            return room.tileDef.walkability[localY][localX];
        }
    }
    return 0; // Not in any room
}

// Check if movement between two positions with different layers is allowed
function canTransitionLayers(fromX, fromY, toX, toY) {
    const fromLayer = getTileLayer(fromX, fromY);
    const toLayer = getTileLayer(toX, toY);

    // Same layer or one is a stair
    if (fromLayer === toLayer || fromLayer === 9 || toLayer === 9) {
        return true;
    }

    // Different layers - check if there's a stair adjacent
    const layerDiff = Math.abs(fromLayer - toLayer);
    if (layerDiff > 1) {
        return false; // Can't jump more than 1 layer
    }

    // Check if there's a stair (9) adjacent to either position
    const adjacentPositions = [
        { x: fromX - 1, y: fromY },
        { x: fromX + 1, y: fromY },
        { x: fromX, y: fromY - 1 },
        { x: fromX, y: fromY + 1 },
        { x: toX - 1, y: toY },
        { x: toX + 1, y: toY },
        { x: toX, y: toY - 1 },
        { x: toX, y: toY + 1 }
    ];

    for (const pos of adjacentPositions) {
        if (getTileLayer(pos.x, pos.y) === 9) {
            return true; // Found a stair
        }
    }

    return false; // No stair found
}

// Determine if a door should be placed between two tile types
function shouldPlaceDoor(tileTypeA, tileTypeB) {
    // Doors only between Room ↔ Corridor
    return (tileTypeA === 'room' && tileTypeB === 'corridor') ||
        (tileTypeA === 'corridor' && tileTypeB === 'room');
}

// Validate walkability alignment between two tiles at their connection point
// Returns: { valid: boolean } - whether the tiles can connect
function validateWalkabilityAlignment(tileDefA, posA, tileDefB, posB, exitSide) {
    // Get the edge cells that will connect
    const edgeA = getEdgeCells(tileDefA, exitSide);
    const edgeB = getEdgeCells(tileDefB, getOppositeSide(exitSide));

    console.log(`[ALIGN] Checking ${tileDefA.id} (${exitSide}) → ${tileDefB.id}`);
    console.log(`[ALIGN] EdgeA (${tileDefA.id}):`, edgeA);
    console.log(`[ALIGN] EdgeB (${tileDefB.id}):`, edgeB);

    // For now, we just check that both edges have at least some walkable cells
    // The actual alignment will be handled manually by the user
    const hasWalkableA = edgeA.some(cell => cell > 0);
    const hasWalkableB = edgeB.some(cell => cell > 0);

    if (!hasWalkableA || !hasWalkableB) {
        console.warn('[ALIGN] One or both edges have no walkable cells');
        return { valid: false };
    }

    console.log(`✓ [ALIGN] Both edges have walkable cells - connection allowed`);
    return { valid: true };
}

// Get edge cells from a tile definition for a given side
function getEdgeCells(tileDef, side) {
    const { walkability, width, height } = tileDef;
    const cells = [];

    switch (side) {
        case 'N':
            // Top row
            for (let x = 0; x < width; x++) {
                cells.push(walkability[0][x]);
            }
            break;
        case 'S':
            // Bottom row
            for (let x = 0; x < width; x++) {
                cells.push(walkability[height - 1][x]);
            }
            break;
        case 'E':
            // Right column
            for (let y = 0; y < height; y++) {
                cells.push(walkability[y][width - 1]);
            }
            break;
        case 'W':
            // Left column
            for (let y = 0; y < height; y++) {
                cells.push(walkability[y][0]);
            }
            break;
    }

    return cells;
}

// Get opposite side
function getOppositeSide(side) {
    const opposites = { 'N': 'S', 'S': 'N', 'E': 'W', 'W': 'E' };
    return opposites[side];
}

// Check if two tiles can connect based on type rules and walkability alignment
// Returns: { valid: boolean } - whether the tiles can connect
function canConnectTiles(roomA, tileDefB, exitSide) {
    const tileDefA = roomA.tileDef;

    // Check type compatibility
    const typeA = tileDefA.tileType;
    const typeB = tileDefB.tileType;

    const validConnections = {
        'room': ['room', 'corridor'],
        'corridor': ['room', 'corridor', 'L', 'T'],
        'L': ['corridor'],
        'T': ['corridor']
    };

    if (!validConnections[typeA] || !validConnections[typeA].includes(typeB)) {
        console.warn(`Invalid connection: ${typeA} cannot connect to ${typeB}`);
        return { valid: false };
    }

    // CRITICAL: Check that tileB has an exit in the opposite direction
    // If we exit through N, the new tile must have S in its exits
    const requiredExit = getOppositeSide(exitSide);
    if (!tileDefB.exits.includes(requiredExit)) {
        console.warn(`Exit direction mismatch: ${tileDefB.id} doesn't have required exit '${requiredExit}' (exiting via '${exitSide}')`);
        return { valid: false };
    }

    // Check walkability alignment
    const alignmentResult = validateWalkabilityAlignment(tileDefA, roomA.tile, tileDefB, null, exitSide);
    if (!alignmentResult.valid) {
        console.warn('Walkability alignment failed');
        return { valid: false };
    }

    return { valid: true };
}

// --- CREACIÓN DE MARCADORES ---
function createPathMarker(stepNumber) {
    const canvas = document.createElement('canvas');
    canvas.width = 128;
    canvas.height = 128;
    const ctx = canvas.getContext('2d');

    ctx.fillStyle = 'rgba(255, 255, 0, 0.5)';
    ctx.fillRect(0, 0, 128, 128);

    ctx.strokeStyle = 'rgba(255, 200, 0, 0.8)';
    ctx.lineWidth = 10;
    ctx.strokeRect(0, 0, 128, 128);

    ctx.fillStyle = '#000000';
    ctx.font = 'bold 60px Arial';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(stepNumber.toString(), 64, 64);

    const texture = new THREE.CanvasTexture(canvas);
    texture.minFilter = THREE.LinearFilter;

    const geometry = new THREE.PlaneGeometry(CONFIG.CELL_SIZE * 0.9, CONFIG.CELL_SIZE * 0.9);
    const material = new THREE.MeshBasicMaterial({
        map: texture,
        transparent: true,
        side: THREE.DoubleSide
    });

    const mesh = new THREE.Mesh(geometry, material);
    mesh.rotation.x = -Math.PI / 2;
    mesh.position.y = 0.05;
    return mesh;
}

function updatePathVisuals() {
    SESSION.pathMeshes.forEach(mesh => scene.remove(mesh));
    SESSION.pathMeshes = [];

    SESSION.path.forEach((pos, index) => {
        const marker = createPathMarker(index + 1);
        const worldPos = gridToWorld(pos.x, pos.y);
        marker.position.x = worldPos.x;
        marker.position.z = worldPos.z;
        scene.add(marker);
        SESSION.pathMeshes.push(marker);
    });
}

// --- MANEJO VISUAL DE SELECCIÓN ---
function updateSelectionVisuals() {
    // Unidades
    ROOMS.visitedRooms.forEach(roomId => {
        const room = ROOMS.rooms.find(r => r.id === roomId);
        if (!room) return;

        room.entities.forEach(entity => {
            if (!entity.mesh) return;

            if (entity.id === SESSION.selectedUnitId) {
                entity.mesh.material.color.setHex(0xffff00);
                entity.mesh.material.opacity = 0.5;
                entity.mesh.material.transparent = true;
            } else {
                entity.mesh.material.color.setHex(0xffffff);
                entity.mesh.material.opacity = 1.0;
            }
        });
    });

    // Puertas
    Object.keys(SESSION.roomMeshes).forEach(roomId => {
        const roomData = SESSION.roomMeshes[roomId];
        if (roomData.doors) {
            roomData.doors.forEach(doorMesh => {
                // Asumimos que guardamos el ID de la puerta en userData al crear el mesh
                if (doorMesh.userData.id === SESSION.selectedDoorId) {
                    doorMesh.material.color.setHex(0xffff00);
                    doorMesh.material.opacity = 0.5;
                    doorMesh.material.transparent = true;
                } else {
                    doorMesh.material.color.setHex(0xffffff);
                    // Restaurar opacidad original (si era transparente) o 1.0
                    // Por simplicidad, puertas cerradas opacas, abiertas transparentes? 
                    // No, el modal decide. Dejamos como estaba por defecto.
                    doorMesh.material.opacity = 1.0;
                }
            });
        }
    });
}

// --- LOGICA MODAL PUERTAS ---
const modal = document.getElementById('door-modal');
const btnYes = document.getElementById('btn-open-yes');
const btnNo = document.getElementById('btn-open-no');

btnYes.addEventListener('click', confirmOpenDoor);
btnNo.addEventListener('click', closeDoorModal);

function openDoorModal() {
    modal.classList.remove('hidden');
}

function closeDoorModal() {
    modal.classList.add('hidden');
    // Deseleccionar si cancela
    if (SESSION.selectedDoorId) {
        SESSION.selectedDoorId = null;
        updateSelectionVisuals();
    }
}

function confirmOpenDoor() {
    if (!SESSION.selectedDoorId) return;

    // Buscar la puerta
    let targetDoor = null;
    let originRoom = null;

    for (const room of ROOMS.rooms) {
        const found = room.doors.find(d => d.id === SESSION.selectedDoorId);
        if (found) {
            targetDoor = found;
            originRoom = room;
            break;
        }
    }

    if (targetDoor && originRoom) {
        console.log("Abriendo puerta:", targetDoor.id);
        const originalDoorId = targetDoor.id; // Guardar ID original para buscar el mesh luego
        targetDoor.isOpen = true;

        // Revelar sala destino (o generarla si no existe)
        if (!targetDoor.leadsTo) {
            // FASE DE EXPLORACIÓN: Generar nueva sala
            console.log("Explorando nueva zona...");
            turnManager.setPhase(PHASES.EXPLORATION);
            const newRoom = exploreRoom(originRoom, targetDoor);
            if (newRoom) {
                targetDoor.leadsTo = newRoom.id;
                targetDoor.id = `door_${originRoom.id}_to_${newRoom.id}`; // Update ID

                // Actualizar también la puerta inversa en la nueva sala
                const oppDoor = newRoom.doors.find(d => d.leadsTo === originRoom.id);
                if (oppDoor) {
                    oppDoor.isOpen = true;
                }
            } else {
                console.log("No se pudo generar sala (bloqueado)");
                // Feedback visual de bloqueo
                const toast = document.createElement('div');
                toast.style.position = 'absolute';
                toast.style.top = '50%';
                toast.style.left = '50%';
                toast.style.transform = 'translate(-50%, -50%)';
                toast.style.background = 'rgba(100, 0, 0, 0.8)';
                toast.style.color = 'white';
                toast.style.padding = '20px';
                toast.style.border = '2px solid red';
                toast.style.fontSize = '20px';
                toast.innerText = "¡CAMINO BLOQUEADO!";
                document.body.appendChild(toast);
                setTimeout(() => toast.remove(), 2000);

                targetDoor.isOpen = false; // Mantener cerrada
                targetDoor.isBlocked = true; // Marcar como bloqueada permanentemente

                // Resetear estado
                SESSION.selectedDoorId = null;
                updateSelectionVisuals();
                closeDoorModal();
                return; // Cancelar apertura
            }
        }

        const targetRoom = ROOMS.rooms.find(r => r.id === targetDoor.leadsTo);

        // Revelar sala destino
        if (targetRoom && !ROOMS.visitedRooms.has(targetRoom.id)) {
            ROOMS.visitedRooms.add(targetRoom.id);
            renderRoom(targetRoom);
        }

        // Actualizar visual del mesh (hacerla invisible o rotarla)
        // Buscamos el mesh en roomMeshes usando el ID ORIGINAL (porque el del objeto puede haber cambiado)
        if (SESSION.roomMeshes[originRoom.id]) {
            const doorMesh = SESSION.roomMeshes[originRoom.id].doors.find(m => m.userData.id === originalDoorId);
            if (doorMesh) {
                doorMesh.visible = false; // "Abrir" visualmente desapareciendo
                doorMesh.userData.id = targetDoor.id; // Sincronizar ID del mesh con el nuevo ID
            }
        }

        // Limipiar selección y cerrar modal
        SESSION.selectedDoorId = null;
        updateSelectionVisuals();
        closeDoorModal();
        drawMinimap();
    }
}

function checkDoorInteraction(unit) {
    if (!SESSION.selectedDoorId) return;

    // Buscar puerta seleccionada
    let targetDoor = null;
    let room = null;
    for (const r of ROOMS.rooms) {
        targetDoor = r.doors.find(d => d.id === SESSION.selectedDoorId);
        if (targetDoor) {
            room = r;
            break;
        }
    }

    if (targetDoor && !targetDoor.isOpen) {
        const doorPos = getDoorGridPosition(room, targetDoor);

        // Verificar adyacencia
        if (isAdjacent({ x: unit.x, y: unit.y }, doorPos)) {
            if (targetDoor.isBlocked) {
                // Mostrar aviso de bloqueo
                const toast = document.createElement('div');
                toast.style.position = 'absolute';
                toast.style.top = '50%';
                toast.style.left = '50%';
                toast.style.transform = 'translate(-50%, -50%)';
                toast.style.background = 'rgba(100, 0, 0, 0.8)';
                toast.style.color = 'white';
                toast.style.padding = '10px';
                toast.style.border = '1px solid red';
                toast.innerText = "¡Puerta bloqueada!";
                document.body.appendChild(toast);
                setTimeout(() => toast.remove(), 1000);
            } else {
                openDoorModal();
            }
        }
    }
}

// --- ANIMACIÓN DE MOVIMIENTO ---
async function animateMovement() {
    if (SESSION.path.length === 0 || !SESSION.selectedUnitId) return;

    SESSION.isAnimating = true;

    // Buscar la entidad en todas las salas
    let unit = null;
    let unitRoom = null;
    for (const room of ROOMS.rooms) {
        unit = room.entities.find(e => e.id === SESSION.selectedUnitId);
        if (unit) {
            unitRoom = room;
            break;
        }
    }

    if (!unit || !unit.mesh) {
        SESSION.isAnimating = false;
        return;
    }

    const pathCopy = [...SESSION.path];

    const animateStep = (targetGridPos) => {
        return new Promise((resolve) => {
            const startPos = { x: unit.mesh.position.x, z: unit.mesh.position.z };
            const targetWorldPos = gridToWorld(targetGridPos.x, targetGridPos.y);
            const endPos = { x: targetWorldPos.x, z: targetWorldPos.z };

            const duration = 200;
            const startTime = Date.now();
            const standeeHeight = ASSETS.standees[unit.type].height;

            const hop = () => {
                const elapsed = Date.now() - startTime;
                const progress = Math.min(elapsed / duration, 1);

                const eased = progress < 0.5
                    ? 2 * progress * progress
                    : 1 - Math.pow(-2 * progress + 2, 2) / 2;

                unit.mesh.position.x = startPos.x + (endPos.x - startPos.x) * eased;
                unit.mesh.position.z = startPos.z + (endPos.z - startPos.z) * eased;

                // Salto visual más sutil
                const hopHeight = 0.5;
                const hopProgress = Math.sin(progress * Math.PI);
                unit.mesh.position.y = (standeeHeight / 2) + (hopProgress * hopHeight);

                if (progress < 1) {
                    requestAnimationFrame(hop);
                } else {
                    unit.mesh.position.x = endPos.x;
                    unit.mesh.position.z = endPos.z;
                    unit.mesh.position.y = standeeHeight / 2;
                    resolve();
                }
            };

            hop();
        });
    };

    for (let i = 0; i < pathCopy.length; i++) {
        const step = pathCopy[i];

        await animateStep(step);

        unit.x = step.x;
        unit.y = step.y;

        // YA NO USAMOS checkDoorTransition automática para revelar/teletransportar
        // en su lugar usamos la lógica de puertas interactivas

        // 2. AUTO-CORRECCIÓN: Seguir usándola por seguridad si entramos
        const actualRoom = detectRoomChange(unit, unitRoom);
        if (actualRoom) {
            unitRoom = actualRoom;
        }

        SESSION.path.shift();
        updatePathVisuals();
    }

    // Al terminar movimiento, verificar interacción con puerta
    checkDoorInteraction(unit);

    // Centrar cámara en el personaje manteniendo el offset de la vista actual
    const newTarget = unit.mesh.position.clone();
    newTarget.y = 0;

    const currentOffset = camera.position.clone().sub(controls.target);

    controls.target.copy(newTarget);
    camera.position.copy(newTarget).add(currentOffset);

    SESSION.selectedUnitId = null; // Deseleccionar unidad al terminar de mover
    updateSelectionVisuals();
    SESSION.isAnimating = false;

    // Auto-exploration check for open passages (L/T tiles)
    checkAutoExploration(unit);

    drawMinimap(); // Actualizar posición final del jugador
}

// Check if unit is on an open passage leading to unexplored area
function checkAutoExploration(unit) {
    // Find unit's current room
    const currentRoom = ROOMS.rooms.find(r => r.entities.some(e => e.id === unit.id));
    if (!currentRoom) return;

    // DEBUG: Log unit position for troubleshooting
    console.log(`[AutoExplore] Unit at (${unit.x}, ${unit.y}) in Room ${currentRoom.id}`);

    const door = currentRoom.doors.find(d => {
        const doorGlobalX = currentRoom.tile.x + d.gridX;
        const doorGlobalY = currentRoom.tile.y + d.gridY;

        // DEBUG: Log each door check
        console.log(`[CheckDoor] ${d.id} (${d.side}): Global(${doorGlobalX}, ${doorGlobalY}) vs Unit(${unit.x}, ${unit.y})`);

        // Check 1: Is Unit on the door line? (Internal or External adjacent)
        let isMainAxisMatch = false;
        let isCrossAxisMatch = false;

        if (d.side === 'N' || d.side === 'S') {
            // Main axis = Y. Internal: doorGlobalY. External: +/- 1
            const deltaY = unit.y - doorGlobalY;
            // Allow 0 (internal) or 1 step out (external)
            if (d.side === 'N') isMainAxisMatch = (deltaY === 0 || deltaY === -1);
            else isMainAxisMatch = (deltaY === 0 || deltaY === 1);

            // Cross axis = X. Allow +/- 1 for corridor width
            isCrossAxisMatch = Math.abs(unit.x - doorGlobalX) <= 1;

        } else { // E or W
            // Main axis = X. Internal: doorGlobalX. External: +/- 1
            const deltaX = unit.x - doorGlobalX;
            // Allow 0 (internal) or 1 step out (external)
            if (d.side === 'W') isMainAxisMatch = (deltaX === 0 || deltaX === -1);
            else isMainAxisMatch = (deltaX === 0 || deltaX === 1);

            // Cross axis = Y. Allow +/- 1 for corridor width
            isCrossAxisMatch = Math.abs(unit.y - doorGlobalY) <= 1;
        }

        if (isMainAxisMatch && isCrossAxisMatch) {
            console.log(`[AutoExplore] MATCH! Door ${d.id} (${d.side}). Unit(${unit.x}, ${unit.y}) vs Door(${doorGlobalX}, ${doorGlobalY})`);
            return true;
        }
        return false;
    });

    if (door) {
        console.log(`[AutoExplore] Found door at unit pos: ${door.id}, Open: ${door.isOpen}, LeadsTo: ${door.leadsTo}`);

        if (door.isOpen && door.leadsTo === null) {
            console.log("[AutoExplore] Triggering exploration...");
            // Use timeout to allow movement animation to settle/feel natural
            setTimeout(() => {
                const newRoom = exploreRoom(currentRoom, door);
                if (newRoom) {
                    renderRoom(newRoom);
                }
            }, 100);
        }
    }
}

// Verifica si la unidad ha entrado físicamente en una sala diferente a la registrada
function detectRoomChange(unit, currentLogicalRoom) {
    for (const room of ROOMS.rooms) {
        if (room.id === currentLogicalRoom.id) continue;

        if (isPositionInRoom(unit.x, unit.y, room)) {
            console.log(`CORRECCIÓN: Entidad detectada en sala ${room.id} (registrada en ${currentLogicalRoom.id}). Transfiriendo...`);

            // Transferir entidad lógica
            const oldIdx = currentLogicalRoom.entities.indexOf(unit);
            if (oldIdx > -1) currentLogicalRoom.entities.splice(oldIdx, 1);
            room.entities.push(unit);

            // Transferir mesh (para renderizado/borrado correcto)
            if (SESSION.roomMeshes[currentLogicalRoom.id]) {
                const meshIdx = SESSION.roomMeshes[currentLogicalRoom.id].entities.indexOf(unit.mesh);
                if (meshIdx > -1) SESSION.roomMeshes[currentLogicalRoom.id].entities.splice(meshIdx, 1);
            }
            if (SESSION.roomMeshes[room.id]) {
                SESSION.roomMeshes[room.id].entities.push(unit.mesh);
            }

            // Asegurar que la sala está visitada y renderizada (si llegamos aquí por "magia")
            if (!ROOMS.visitedRooms.has(room.id)) {
                ROOMS.visitedRooms.add(room.id);
                renderRoom(room);
            }

            ROOMS.currentRoom = room.id;
            drawMinimap();

            return room; // Devolver la nueva sala actual
        }
    }
    return null;
}

// --- VERIFICAR TRANSICIÓN DE PUERTA ---
function checkDoorTransition(unit, currentRoom) {
    for (const door of currentRoom.doors) {
        const doorGridPos = getDoorGridPosition(currentRoom, door);

        if (unit.x === doorGridPos.x && unit.y === doorGridPos.y) {
            const targetRoomId = door.leadsTo;

            // Solo nos encargamos de precargar/revelar la sala aquí
            if (!ROOMS.visitedRooms.has(targetRoomId)) {
                console.log("Puerta pisada -> Revelando sala", targetRoomId);
                ROOMS.visitedRooms.add(targetRoomId);
                const targetRoom = ROOMS.rooms.find(r => r.id === targetRoomId);
                if (targetRoom) {
                    renderRoom(targetRoom);
                    drawMinimap();
                }
            }
            break;
        }
    }
}


function getDoorGridPosition(room, door) {
    const tile = room.tile;
    const tileDef = room.tileDef;
    if (!tileDef) {
        console.error("Room", room.id, "has no tileDef in getDoorGridPosition!");
        return { x: tile.x, y: tile.y };
    }

    const tileWidth = tileDef.width;
    const tileHeight = tileDef.height;

    switch (door.side) {
        case 'N':
            return { x: tile.x + door.gridX, y: tile.y - 1 };
        case 'S':
            return { x: tile.x + door.gridX, y: tile.y + tileHeight };
        case 'E':
            return { x: tile.x + tileWidth, y: tile.y + door.gridY };
        case 'W':
            return { x: tile.x - 1, y: tile.y + door.gridY };
    }
}

// Calcula la posición completa de la puerta en el mundo 3D
// Devuelve: { worldPos: {x, z}, meshPos: {x, y, z}, rotation: number, wallOffset: number }
function getDoorWorldPosition(room, door, centerX, centerZ, halfSizeX, halfSizeZ) {
    const doorGridPos = getDoorGridPosition(room, door);
    const doorWorldPos = gridToWorld(doorGridPos.x, doorGridPos.y);

    const doorHeight = 2.0;
    let meshPos = { x: 0, y: doorHeight / 2, z: 0 };
    let rotation = 0;
    let wallOffset = 0;

    switch (door.side) {
        case 'N':
            // Pared Norte: puerta alineada en X, Z en el borde norte
            meshPos.x = doorWorldPos.x;
            meshPos.z = centerZ - halfSizeZ;
            rotation = 0;
            // Offset relativo al centro de la pared (para el hueco)
            wallOffset = doorWorldPos.x - centerX;
            break;
        case 'S':
            // Pared Sur: puerta alineada en X, Z en el borde sur
            meshPos.x = doorWorldPos.x;
            meshPos.z = centerZ + halfSizeZ;
            rotation = 0;
            // Para pared Sur, el offset es directo (sin inversión)
            wallOffset = doorWorldPos.x - centerX;
            break;
        case 'E':
            // Pared Este: puerta alineada en Z, X en el borde este
            meshPos.x = centerX + halfSizeX;
            meshPos.z = doorWorldPos.z;
            rotation = Math.PI / 2;
            // Offset relativo al centro de la pared
            // Con rotation=π/2, el eje X local apunta hacia -Z, entonces:
            // offset_local = -(doorZ - centerZ)
            wallOffset = -(doorWorldPos.z - centerZ);
            break;
        case 'W':
            // Pared Oeste: puerta alineada en Z, X en el borde oeste
            meshPos.x = centerX - halfSizeX;
            meshPos.z = doorWorldPos.z;
            rotation = Math.PI / 2;
            // Con rotation=π/2, el eje X local apunta hacia -Z (igual que pared E)
            // Por tanto, también necesita offset invertido
            wallOffset = -(doorWorldPos.z - centerZ);
            break;
    }

    return {
        worldPos: doorWorldPos,
        meshPos: meshPos,
        rotation: rotation,
        wallOffset: wallOffset
    };
}

// --- CARGA Y RENDERIZADO ---
const textureLoader = new THREE.TextureLoader();

function loadTexture(path) {
    if (SESSION.textureCache[path]) {
        return Promise.resolve(SESSION.textureCache[path]);
    }

    return new Promise((resolve) => {
        textureLoader.load(path, (tex) => {
            tex.colorSpace = THREE.SRGBColorSpace;
            tex.magFilter = THREE.NearestFilter;
            tex.minFilter = THREE.NearestFilter;
            SESSION.textureCache[path] = tex;
            resolve(tex);
        });
    });
}

async function renderRoom(room) {
    console.log(">>> renderRoom ejecutado para sala:", room.id);
    if (SESSION.roomMeshes[room.id]) {
        return; // Ya renderizada
    }

    const roomMeshes = {
        tile: null,
        walls: [],
        doors: [],
        entities: []
    };

    // Renderizar tile usando la nueva definición
    const tileDef = room.tileDef;
    if (!tileDef) {
        console.error("Room", room.id, "has no tileDef!");
        return;
    }

    const baseTex = await loadTexture(tileDef.image);
    const tileTex = baseTex.clone(); // CLONAR para no afectar a otras salas
    tileTex.needsUpdate = true; // Asegurar que Three.js sepa que es nueva

    tileTex.wrapS = THREE.RepeatWrapping;
    tileTex.wrapT = THREE.RepeatWrapping;

    // No repetir la textura - cada tile tiene su propia imagen completa
    tileTex.repeat.set(1, 1);

    const worldWidth = tileDef.width * CONFIG.CELL_SIZE;
    const worldHeight = tileDef.height * CONFIG.CELL_SIZE;

    const tileGeometry = new THREE.PlaneGeometry(worldWidth, worldHeight);
    const tileMaterial = new THREE.MeshStandardMaterial({
        map: tileTex,
        transparent: true,
        side: THREE.DoubleSide
    });
    const tileMesh = new THREE.Mesh(tileGeometry, tileMaterial);

    tileMesh.rotation.x = -Math.PI / 2;
    tileMesh.receiveShadow = true;

    const originPos = gridToWorld(room.tile.x, room.tile.y);
    console.log(`[DEBUG] renderRoom ${room.id} | Tile:`, room.tile, `| WorldOrigin:`, originPos);

    tileMesh.position.x = originPos.x + (worldWidth / 2) - (CONFIG.CELL_SIZE / 2);
    tileMesh.position.z = originPos.z + (worldHeight / 2) - (CONFIG.CELL_SIZE / 2);
    tileMesh.position.y = 0;

    // Random rotation for 4x4 rooms to add variety
    if (tileDef.id.includes('room_4x4')) {
        const rotations = [0, Math.PI / 2, Math.PI, Math.PI * 1.5];
        const randomRotation = rotations[Math.floor(Math.random() * rotations.length)];
        tileMesh.rotation.z = randomRotation;
        console.log(`[DEBUG] Room ${room.id} rotated ${(randomRotation * 180 / Math.PI).toFixed(0)}°`);
    }

    console.log(`[DEBUG] renderRoom ${room.id} | MeshPos:`, tileMesh.position);

    scene.add(tileMesh);
    roomMeshes.tile = tileMesh;

    // Renderizar paredes
    const wallTex = await loadTexture(ASSETS.tiles['wall_1'].src);
    wallTex.wrapS = THREE.RepeatWrapping;
    wallTex.wrapT = THREE.RepeatWrapping;
    // Las paredes siempre repiten en horizontal, la V es fija
    wallTex.repeat.set(2, 2);

    const wallHeight = 2.5;
    const halfSizeX = worldWidth / 2;
    const halfSizeZ = worldHeight / 2;
    const centerX = tileMesh.position.x;
    const centerZ = tileMesh.position.z;

    const wallConfigs = [
        { side: 'N', width: worldWidth, offset: { x: 0, z: -halfSizeZ }, rotation: 0 },
        { side: 'S', width: worldWidth, offset: { x: 0, z: halfSizeZ }, rotation: 0 },
        { side: 'E', width: worldHeight, offset: { x: halfSizeX, z: 0 }, rotation: Math.PI / 2 },
        { side: 'W', width: worldHeight, offset: { x: -halfSizeX, z: 0 }, rotation: Math.PI / 2 }
    ];

    // Calcular posiciones de puertas para procesar paredes
    // Mapa: Side -> Door (solo soportamos 1 puerta por pared por ahora para simplificar)
    const doorsOnSides = {};
    room.doors.forEach(d => { doorsOnSides[d.side] = d; });

    for (const config of wallConfigs) {
        const wallSide = config.side;

        // Skip wall rendering for L and T tiles on their exit sides
        if ((room.tileDef.tileType === 'L' || room.tileDef.tileType === 'T') &&
            room.tileDef.exits.includes(wallSide)) {
            console.log(`Skipping wall on ${wallSide} for ${room.tileDef.tileType} tile (exit side)`);
            continue; // Don't render wall on exit sides
        }

        const door = doorsOnSides[wallSide];

        // Función helper para crear un segmento de pared
        const createWallSegment = (w, h, xOffset, yOffset, opacity, name) => {
            if (w <= 0.01) return; // Evitar segmentos degenerados

            const segmentGeometry = new THREE.PlaneGeometry(w, h);

            // Ajustar textura al tamaño del segmento
            const segmentTex = wallTex.clone();
            segmentTex.wrapS = THREE.RepeatWrapping;
            segmentTex.wrapT = THREE.RepeatWrapping;
            segmentTex.repeat.set(w / 2, h / (wallHeight / 2)); // Mantener densidad aprox

            const segmentMaterial = new THREE.MeshStandardMaterial({
                map: segmentTex,
                transparent: opacity < 1.0,
                opacity: opacity,
                side: THREE.DoubleSide
            });

            const wall = new THREE.Mesh(segmentGeometry, segmentMaterial);

            // Calculamos posición RELATIVA al centro de la pared "ideal"
            // La pared ideal está en config.offset
            // Rotamos el offset local del segmento según la rotación de la pared

            const localX = xOffset;
            const localZ = 0; // En el plano de la pared

            // Rotar vector (localX, 0) por config.rotation
            // Plane geometry is created at origin. We rotate it around Y.
            // A segment meant to be at "xOffset" along the plane's width needs to be translated.

            // Posición de la pared "Base"
            const baseX = centerX + config.offset.x;
            const baseZ = centerZ + config.offset.z;

            // Vector dirección de la pared (Hacia la derecha de la pared)
            // PlaneGeometry +X is "Right"
            const dirX = Math.cos(config.rotation);
            const dirZ = -Math.sin(config.rotation);

            wall.position.x = baseX + (dirX * xOffset);
            wall.position.z = baseZ + (dirZ * xOffset);
            wall.position.y = yOffset; // Altura absoluta

            wall.rotation.y = config.rotation;
            wall.castShadow = true;
            wall.receiveShadow = true;
            wall.userData.wallSide = config.side;
            scene.add(wall);
            roomMeshes.walls.push(wall);
        };

        const opacity = getWallOpacity(config.side, SESSION.currentView); // Se actualiza dinámicamente

        if (!door) {
            // PARED SOLIDA (Caso original simplificado)
            createWallSegment(config.width, wallHeight, 0, wallHeight / 2, opacity, "FullWall");
        } else {
            // PARED CON HUECO
            const doorWidth = 1.5;
            const doorHeight = 2.0;

            // Usar función unificada para obtener la posición de la puerta
            const doorInfo = getDoorWorldPosition(room, door, centerX, centerZ, halfSizeX, halfSizeZ);
            const doorOffset = doorInfo.wallOffset;

            const w = config.width;

            // Segmento Izquierdo: Desde -w/2 hasta (doorOffset - doorWidth/2)
            const leftEnd = doorOffset - (doorWidth / 2);
            const leftStart = -w / 2;
            const leftWidth = leftEnd - leftStart;
            const leftCenter = leftStart + (leftWidth / 2);

            createWallSegment(leftWidth, wallHeight, leftCenter, wallHeight / 2, opacity, "LeftSeg");

            // Segmento Derecho: Desde (doorOffset + doorWidth/2) hasta w/2
            const rightStart = doorOffset + (doorWidth / 2);
            const rightEnd = w / 2;
            const rightWidth = rightEnd - rightStart;
            const rightCenter = rightStart + (rightWidth / 2);

            createWallSegment(rightWidth, wallHeight, rightCenter, wallHeight / 2, opacity, "RightSeg");

            // Dintel (Arriba de la puerta)
            const lintelHeight = wallHeight - doorHeight;
            if (lintelHeight > 0) {
                createWallSegment(doorWidth, lintelHeight, doorOffset, doorHeight + (lintelHeight / 2), opacity, "Lintel");
            }
        }
    }

    // Renderizar puertas
    const doorTex = await loadTexture(ASSETS.tiles['door_1'].src);
    const doorWidth = 1.5;
    const doorHeight = 2.0;

    for (const door of room.doors) {
        // Renderizar puerta independientemente de si tiene destino conocido o no
        // (Las puertas leadsTo: null son zonas inexploradas)

        const doorGeometry = new THREE.PlaneGeometry(doorWidth, doorHeight);
        const doorMaterial = new THREE.MeshStandardMaterial({
            map: doorTex.clone(),
            transparent: true,
            alphaTest: 0.1,
            side: THREE.DoubleSide
        });

        const doorMesh = new THREE.Mesh(doorGeometry, doorMaterial);
        doorMesh.userData.id = door.id;
        doorMesh.visible = !door.isOpen; // Ocultar si ya está abierta

        // Usar función unificada para posicionar la puerta
        const doorInfo = getDoorWorldPosition(room, door, centerX, centerZ, halfSizeX, halfSizeZ);
        doorMesh.position.set(doorInfo.meshPos.x, doorInfo.meshPos.y, doorInfo.meshPos.z);
        doorMesh.rotation.y = doorInfo.rotation;

        scene.add(doorMesh);
        roomMeshes.doors.push(doorMesh);
    }

    // Renderizar entidades
    for (const entity of room.entities) {
        // Si la entidad ya tiene mesh (vino de otra sala), solo añadirlo al tracking
        if (entity.mesh) {
            roomMeshes.entities.push(entity.mesh);
            continue;
        }

        const standeeDef = ASSETS.standees[entity.type];
        const standeeTex = await loadTexture(standeeDef.src);

        const imgAspect = standeeTex.image.width / standeeTex.image.height;
        const height = standeeDef.height;
        const width = height * imgAspect;

        const standeeGeometry = new THREE.PlaneGeometry(width, height);
        const standeeMaterial = new THREE.MeshStandardMaterial({
            map: standeeTex,
            transparent: true,
            alphaTest: 0.5,
            side: THREE.DoubleSide
        });

        const standeeMesh = new THREE.Mesh(standeeGeometry, standeeMaterial);
        standeeMesh.castShadow = true;

        const pos = gridToWorld(entity.x, entity.y);
        standeeMesh.position.set(pos.x, height / 2, pos.z);

        scene.add(standeeMesh);
        entity.mesh = standeeMesh;
        roomMeshes.entities.push(standeeMesh);
    }

    SESSION.roomMeshes[room.id] = roomMeshes;
}

async function initWorld() {
    // Renderizar solo las salas visitadas
    for (const roomId of ROOMS.visitedRooms) {
        const room = ROOMS.rooms.find(r => r.id === roomId);
        if (room) {
            await renderRoom(room);
        }
    }
}

initWorld();

// --- COMPASS UI ---
function updateCompassUI() {
    document.querySelectorAll('.compass-btn').forEach(btn => {
        btn.classList.remove('active');
    });
    const activeBtn = document.querySelector(`[data-dir="${SESSION.currentView}"]`);
    if (activeBtn) {
        activeBtn.classList.add('active');
    }
}

// --- MINIMAP UI ---
function drawMinimap() {
    const canvas = document.getElementById('minimap');
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const width = canvas.width;
    const height = canvas.height;

    // Limpiar canvas
    ctx.fillStyle = '#111';
    ctx.fillRect(0, 0, width, height);

    if (ROOMS.rooms.length === 0) return;

    // 1. Calcular límites del MAPA COMPLETO (Debug Mode: Ver todo)
    let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;

    ROOMS.rooms.forEach(room => {
        const tileDef = room.tileDef;
        if (!tileDef) return;
        minX = Math.min(minX, room.tile.x);
        maxX = Math.max(maxX, room.tile.x + tileDef.width);
        minY = Math.min(minY, room.tile.y);
        maxY = Math.max(maxY, room.tile.y + tileDef.height);
    });

    // Añadir margen generoso para ver bien
    const margin = 8;
    minX -= margin;
    maxX += margin;
    minY -= margin;
    maxY += margin;

    const mapWidth = maxX - minX;
    const mapHeight = maxY - minY;

    // Calcular escala para encajar TODO el mapa en el canvas
    const scaleX = width / mapWidth;
    const scaleY = height / mapHeight;
    const scale = Math.min(scaleX, scaleY);

    // Función para transformar coords de cuadrícula a canvas
    const toCanvas = (x, y) => {
        return {
            x: (x - minX) * scale + (width - mapWidth * scale) / 2,
            y: (y - minY) * scale + (height - mapHeight * scale) / 2
        };
    };

    // 2. Dibujar TODAS las Salas
    ROOMS.rooms.forEach(room => {
        const tileDef = room.tileDef;
        if (!tileDef) return;

        const pos = toCanvas(room.tile.x, room.tile.y);
        const w = tileDef.width * scale;
        const h = tileDef.height * scale;

        // Color base de sala
        const isVisited = ROOMS.visitedRooms.has(room.id);

        if (room.id === ROOMS.currentRoom) {
            ctx.fillStyle = '#44aadd'; // Actual: Azul
        } else if (isVisited) {
            ctx.fillStyle = '#777';    // Visitada: Gris Claro
        } else {
            ctx.fillStyle = '#333';    // No Visitada: Gris Oscuro
        }

        ctx.fillRect(pos.x, pos.y, w, h);

        // Borde
        ctx.strokeStyle = '#555';
        ctx.lineWidth = 1;
        ctx.strokeRect(pos.x, pos.y, w, h);

        // Puertas
        ctx.fillStyle = isVisited ? '#fff' : '#666'; // Puertas tenues si no visitado
        room.doors.forEach(door => {
            const doorGridPos = getDoorGridPosition(room, door);
            const dPos = toCanvas(doorGridPos.x + 0.5, doorGridPos.y + 0.5);
            ctx.beginPath();
            ctx.arc(dPos.x, dPos.y, scale * 0.4, 0, Math.PI * 2);
            ctx.fill();
        });
    });

    // 3. Dibujar Jugador
    const currentRoomObj = ROOMS.rooms.find(r => r.id === ROOMS.currentRoom);
    if (currentRoomObj) {
        const player = currentRoomObj.entities.find(e => e.type === 'hero_1');
        if (player) {
            const pPos = toCanvas(player.x + 0.5, player.y + 0.5);

            ctx.fillStyle = '#ffff00';
            ctx.beginPath();
            ctx.arc(pPos.x, pPos.y, scale * 0.8, 0, Math.PI * 2);
            ctx.fill();
        }
    }
}

// Event listeners para los botones del compás
document.querySelectorAll('.compass-btn').forEach(btn => {
    btn.addEventListener('click', () => {
        const direction = btn.getAttribute('data-dir');
        if (direction) {
            setCameraView(direction);
            updateCompassUI();
        }
    });
});

// Inicializar minimapa
drawMinimap();

// --- INTERACCIÓN ---
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

window.addEventListener('pointerdown', (event) => {
    if (SESSION.isAnimating) return;

    if (event.button === 0) {
        pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
        pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;

        raycaster.setFromCamera(pointer, camera);

        // Detectar click en entidades
        const allEntities = [];
        ROOMS.visitedRooms.forEach(roomId => {
            const room = ROOMS.rooms.find(r => r.id === roomId);
            if (room) {
                allEntities.push(...room.entities.filter(e => e.mesh));
            }
        });

        const entityMeshes = allEntities.map(e => e.mesh);
        const intersectsEntities = raycaster.intersectObjects(entityMeshes);

        if (intersectsEntities.length > 0) {
            const clickedMesh = intersectsEntities[0].object;
            const entity = allEntities.find(e => e.mesh === clickedMesh);
            if (entity) {
                console.log("Seleccionado:", entity.type);
                SESSION.selectedUnitId = entity.id;
                SESSION.selectedDoorId = null; // Deseleccionar puerta
                SESSION.path = [];
                updatePathVisuals();
                updateSelectionVisuals();
                return;
            }
        }

        // Detectar click en puertas
        const allDoors = [];
        Object.values(SESSION.roomMeshes).forEach(roomData => {
            if (roomData.doors) {
                // Solo incluir puertas visibles (cerradas) en el raycast
                allDoors.push(...roomData.doors.filter(door => door.visible));
            }
        });

        const intersectsDoors = raycaster.intersectObjects(allDoors);
        if (intersectsDoors.length > 0) {
            const clickedDoor = intersectsDoors[0].object;
            if (clickedDoor.userData.id) {
                console.log("Puerta seleccionada:", clickedDoor.userData.id);
                SESSION.selectedDoorId = clickedDoor.userData.id;
                SESSION.selectedUnitId = null;
                SESSION.path = [];
                updatePathVisuals();
                updateSelectionVisuals();

                // Verificar interacción inmediata (si ya estamos al lado)
                // Buscamos al héroe principal (asumimos que es el que controlamos)
                let hero = null;
                for (const r of ROOMS.rooms) {
                    hero = r.entities.find(e => e.type === 'hero_1');
                    if (hero) break;
                }

                if (hero) {
                    checkDoorInteraction(hero);
                }
                return;
            }
        }

        // Procesar click en suelo
        if (SESSION.selectedUnitId) {
            const intersectsGround = raycaster.intersectObject(raycastPlane);

            if (intersectsGround.length > 0) {
                const point = intersectsGround[0].point;
                const gridPos = worldToGrid(point.x, point.z);

                let prevNode;
                if (SESSION.path.length > 0) {
                    prevNode = SESSION.path[SESSION.path.length - 1];
                } else {
                    const entity = allEntities.find(e => e.id === SESSION.selectedUnitId);
                    prevNode = { x: entity.x, y: entity.y };
                }

                if (SESSION.path.length > 0) {
                    const lastNode = SESSION.path[SESSION.path.length - 1];
                    if (lastNode.x === gridPos.x && lastNode.y === gridPos.y) {
                        SESSION.path.pop();
                        updatePathVisuals();
                        return;
                    }
                }

                if (isAdjacent(prevNode, gridPos)) {
                    const alreadyInPath = SESSION.path.some(p => p.x === gridPos.x && p.y === gridPos.y);
                    const isUnitPos = (gridPos.x === prevNode.x && gridPos.y === prevNode.y && SESSION.path.length === 0);

                    // VALIDACIÓN: Solo permitir movimiento a celdas transitables
                    if (!alreadyInPath && !isUnitPos && isWalkable(gridPos.x, gridPos.y)) {
                        SESSION.path.push(gridPos);
                        updatePathVisuals();
                    }
                }
            }
        }
    }

    if (event.button === 2) {
        event.preventDefault();

        if (SESSION.selectedUnitId && SESSION.path.length > 0) {
            animateMovement();
        }
    }
});

window.addEventListener('contextmenu', (event) => {
    event.preventDefault();
});

function animate() {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
}
animate();

window.addEventListener('resize', () => {
    const aspect = window.innerWidth / window.innerHeight;
    camera.left = -d * aspect;
    camera.right = d * aspect;
    camera.top = d;
    camera.bottom = -d;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});
