import * as BABYLON from "babylonjs";
import { findDeadEnds } from "./maze.js";
import { gridCellToWorld, isWalkableCell } from "./grid.js";
import { seededRng } from "./maze.js";
import { createFloorMaterial, createWallMaterial, createChestMaterial, createExitMaterial } from "../assets/materials.js";

const cellSize = 2;

/**
 * Clear all level meshes and associated data
 */
export function clearLevelMeshes(levelMeshes, state) {
    for (const m of levelMeshes) {
        try { m.dispose(); } catch(e) {}
    }
    levelMeshes.length = 0;
    state.chestMap = new Map();
    state.keyChestKey = null;
    state.exitBox = null;
    state.exitGridPos = null;
    state.spawnGridPos = null;
    state.highlightedChest = null;
}

/**
 * Check if cell is reserved (has chest or exit)
 */
function isReservedCell(x, y, state) {
    if (state.chestMap.has(`${x},${y}`)) return true;
    if (state.exitGridPos && state.exitGridPos.x === x && state.exitGridPos.y === y) return true;
    return false;
}

/**
 * Build maze level from grid
 */
export function buildLevelFromGrid(scene, grid, state, levelMeshes) {
    const h = grid.length;
    const w = grid[0].length;
    const halfW = (w * cellSize) / 2;
    const halfH = (h * cellSize) / 2;

    // Floor
    const floor = BABYLON.MeshBuilder.CreateGround('levelGround', { width: w*cellSize, height: h*cellSize }, scene);
    floor.position = new BABYLON.Vector3(0, 0, 0);
    floor.checkCollisions = true;
    floor.material = createFloorMaterial(scene, w, h);
    levelMeshes.push(floor);

    // Walls
    const wallMat = createWallMaterial(scene);
    for (let y = 0; y < h; y++) {
        for (let x = 0; x < w; x++) {
            if (grid[y][x] === 1) {
                const box = BABYLON.MeshBuilder.CreateBox(`wall_${x}_${y}`, { size: cellSize }, scene);
                box.position = new BABYLON.Vector3(x*cellSize - halfW + cellSize/2, cellSize/2, y*cellSize - halfH + cellSize/2);
                box.material = wallMat;
                box.checkCollisions = true;
                levelMeshes.push(box);
            }
        }
    }
}

/**
 * Place chests on dead ends
 */
export function placeChestsOnDeadEnds(scene, grid, deadEnds, minCount, seed, state, levelMeshes) {
    const rng = seededRng(seed);
    for (let i = deadEnds.length - 1; i > 0; i--) {
        const j = Math.floor(rng() * (i+1));
        [deadEnds[i], deadEnds[j]] = [deadEnds[j], deadEnds[i]];
    }
    
    const chosen = deadEnds.slice(0, Math.min(minCount, deadEnds.length));
    const halfW = (grid[0].length * cellSize) / 2;
    const halfH = (grid.length * cellSize) / 2;

    for (const [x, y] of chosen) {
        const c = BABYLON.MeshBuilder.CreateBox(`chest_${x}_${y}`, { width: cellSize*0.8, height: cellSize*0.6, depth: cellSize*0.6 }, scene);
        c.position = new BABYLON.Vector3(x*cellSize - halfW + cellSize/2, cellSize*0.3, y*cellSize - halfH + cellSize/2);
        c.material = createChestMaterial(scene, false);
        c.isPickable = true;
        levelMeshes.push(c);
        state.chestMap.set(`${x},${y}`, { mesh: c, opened: false });
    }

    // Mark one chest as containing the key
    if (chosen.length > 0) {
        const k = Math.floor(rng() * chosen.length);
        const [kx, ky] = chosen[k];
        state.keyChestKey = `${kx},${ky}`;
        const entry = state.chestMap.get(state.keyChestKey);
        if (entry) {
            entry.mesh.material = createChestMaterial(scene, true);
        }
    }
}

/**
 * Place exit door
 */
export function placeExit(scene, grid, seed, state, levelMeshes) {
    const dead = findDeadEnds(grid);
    const rng = seededRng(seed + 1);
    if (dead.length === 0) return;
    
    const availableDead = dead.filter(([x, y]) => !state.chestMap.has(`${x},${y}`));
    if (availableDead.length === 0) {
        const idx = Math.floor(rng() * dead.length);
        state.exitGridPos = { x: dead[idx][0], y: dead[idx][1] };
    } else {
        const idx = Math.floor(rng() * availableDead.length);
        state.exitGridPos = { x: availableDead[idx][0], y: availableDead[idx][1] };
    }
    
    const [x, y] = [state.exitGridPos.x, state.exitGridPos.y];
    if (!isWalkableCell(grid, x, y)) {
        console.warn("Exit selected on non-walkable cell", { x, y });
        return;
    }

    const exitWorld = gridCellToWorld(grid, x, y, cellSize);
    const plane = BABYLON.MeshBuilder.CreatePlane('exitDoor', {
        width: cellSize * 1.35,
        height: cellSize * 1.85,
        sideOrientation: BABYLON.Mesh.DOUBLESIDE,
    }, scene);
    plane.material = createExitMaterial(scene);
    plane.position = new BABYLON.Vector3(exitWorld.x, cellSize * 0.92, exitWorld.z);
    plane.billboardMode = BABYLON.Mesh.BILLBOARDMODE_Y;
    state.exitBox = plane;
    levelMeshes.push(plane);
}

/**
 * Spawn camera at optimal location
 */
export function spawnCameraAt(scene, grid, camera, state) {
    const h = grid.length;
    const w = grid[0].length;
    let bestCell = null;
    let bestDist = -1;

    for (let y = 1; y < h - 1; y++) {
        for (let x = 1; x < w - 1; x++) {
            if (!isWalkableCell(grid, x, y)) continue;
            if (isReservedCell(x, y, state)) continue;

            const d = state.exitGridPos ? Math.hypot(x - state.exitGridPos.x, y - state.exitGridPos.y) : 0;
            if (d > bestDist) {
                bestDist = d;
                bestCell = { x, y };
            }
        }
    }

    if (!bestCell) {
        console.warn("No valid spawn cell found.");
        return;
    }

    state.spawnGridPos = bestCell;
    const spawnWorld = gridCellToWorld(grid, bestCell.x, bestCell.y, cellSize);

    if (camera && camera.position) {
        camera.position = new BABYLON.Vector3(spawnWorld.x, 1.6, spawnWorld.z);
    }
}
