import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { VoxelGrid } from "../grid/VoxelGrid.js";
import { CaveGenerator } from "../generation/CaveGenerator.js";
import { CrystalSpiresGenerator } from "../generation/CrystalSpiresGenerator.js";
import { ContestedFrontierGenerator } from "../generation/ContestedFrontierGenerator.js";
import { VoidSeepDepthsGenerator } from "../generation/VoidSeepDepthsGenerator.js";
import { RustingWastesGenerator } from "../generation/RustingWastesGenerator.js";

class MapVisualizer {
  constructor() {
    this.container = document.body;
    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color(0x202020);

    this.camera = new THREE.PerspectiveCamera(
      60,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    this.camera.position.set(20, 30, 20);

    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    this.container.appendChild(this.renderer.domElement);

    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    this.controls.target.set(10, 0, 10);
    this.controls.update();

    // Lights
    const ambientLight = new THREE.AmbientLight(0x404040);
    this.scene.add(ambientLight);
    const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
    dirLight.position.set(50, 100, 50);
    this.scene.add(dirLight);

    // Helper Grid
    const gridHelper = new THREE.GridHelper(50, 50, 0x444444, 0x222222);
    gridHelper.position.set(10, -0.01, 10); // Center vaguely around 0-20 area
    this.scene.add(gridHelper);

    this.setupUI();
    this.generate();
    this.animate();

    window.addEventListener("resize", () => this.onWindowResize());
  }

  setupUI() {
    this.genSelect = document.getElementById("generatorSelect");
    this.seedInput = document.getElementById("seedInput");
    this.fillInput = document.getElementById("fillInput");
    this.iterInput = document.getElementById("iterInput");
    this.textureToggle = document.getElementById("textureToggle");
    this.zoneToggle = document.getElementById("zoneToggle");
    this.genBtn = document.getElementById("generateBtn");

    // Populate Generator Options
    const generators = [
      "CaveGenerator",
      "CrystalSpiresGenerator",
      "ContestedFrontierGenerator",
      "VoidSeepDepthsGenerator",
      "RustingWastesGenerator",
    ];
    generators.forEach((gen) => {
      const opt = document.createElement("option");
      opt.value = gen;
      opt.textContent = gen;
      this.genSelect.appendChild(opt);
    });

    this.genBtn.addEventListener("click", () => this.generate());
    this.textureToggle.addEventListener("change", () => {
      if (this.lastGrid) this.renderGrid(this.lastGrid);
    });
    this.zoneToggle.addEventListener("change", () => {
      if (this.zoneGroup) this.zoneGroup.visible = this.zoneToggle.checked;
    });

    this.loadFromURL();
  }

  loadFromURL() {
    const params = new URLSearchParams(window.location.search);
    if (params.has("type")) this.genSelect.value = params.get("type");
    if (params.has("seed")) this.seedInput.value = params.get("seed");
    if (params.has("fill")) this.fillInput.value = params.get("fill");
    if (params.has("iter")) this.iterInput.value = params.get("iter");
    if (params.has("textures"))
      this.textureToggle.checked = params.get("textures") === "true";
    if (params.has("zones"))
      this.zoneToggle.checked = params.get("zones") === "true";
  }

  updateURL(type, seed, fill, iter) {
    const params = new URLSearchParams();
    params.set("type", type);
    params.set("seed", seed);
    params.set("fill", fill);
    params.set("iter", iter);
    params.set("textures", this.textureToggle.checked);
    params.set("zones", this.zoneToggle.checked);

    const newUrl = `${window.location.pathname}?${params.toString()}`;
    window.history.replaceState({}, "", newUrl);
  }

  generate() {
    const type = this.genSelect.value;
    const seed = parseInt(this.seedInput.value) || 12345;
    const fill = parseFloat(this.fillInput.value) || 0.45;
    const iter = parseInt(this.iterInput.value) || 4;

    console.log(
      `Generating ${type} with seed=${seed}, fill=${fill}, iter=${iter}`
    );

    this.updateURL(type, seed, fill, iter);

    // 1. Setup Grid
    // Standard size (matches GameLoop default)
    const width = 20;
    const height = 20;
    const depth = 20;
    const grid = new VoxelGrid(width, height, depth);

    // 2. Run Generator
    let generator;
    if (type === "CaveGenerator") {
      generator = new CaveGenerator(grid, seed);
      generator.generate(fill, iter);
    } else if (type === "CrystalSpiresGenerator") {
      generator = new CrystalSpiresGenerator(grid, seed);
      // Use map-visualizer inputs?
      // fill -> spireCount (scaled? or just fixed for now)
      // iter -> numFloors
      // Let's remap slightly for usability:
      // Fill 0.45 -> Spire Count 4
      // Iter 4 -> Floors 4
      const spireCount = Math.floor(fill * 10) || 4;
      const floors = iter || 3;
      generator.generate(spireCount, floors);
    } else if (type === "ContestedFrontierGenerator") {
      generator = new ContestedFrontierGenerator(grid, seed);
      generator.generate();
    } else if (type === "VoidSeepDepthsGenerator") {
      generator = new VoidSeepDepthsGenerator(grid, seed);
      generator.generate(iter || 1);
    } else if (type === "RustingWastesGenerator") {
      generator = new RustingWastesGenerator(grid, seed);
      generator.generate();
    }

    // 2.5 Generate ThreeJS Materials from Palette
    this.generatedMaterials = {};
    if (
      generator &&
      generator.generatedAssets &&
      generator.generatedAssets.palette
    ) {
      this.generateMaterialsFromPalette(generator.generatedAssets.palette);
    }

    // 3. Render
    this.lastGrid = grid;
    this.renderGrid(grid);

    // 4. Render Spawn Zones (if available)
    if (
      generator &&
      generator.generatedAssets &&
      generator.generatedAssets.spawnZones
    ) {
      this.renderZones(generator.generatedAssets.spawnZones);
    } else {
      if (this.zoneGroup) {
        this.scene.remove(this.zoneGroup);
        this.zoneGroup = null;
      }
    }
  }

  generateMaterialsFromPalette(palette) {
    // Disposes old textures if any (optimization)

    for (const [id, maps] of Object.entries(palette)) {
      const materialParams = {};

      if (maps.diffuse)
        materialParams.map = new THREE.CanvasTexture(maps.diffuse);
      if (maps.normal)
        materialParams.normalMap = new THREE.CanvasTexture(maps.normal);
      if (maps.roughness)
        materialParams.roughnessMap = new THREE.CanvasTexture(maps.roughness);
      if (maps.bump)
        materialParams.bumpMap = new THREE.CanvasTexture(maps.bump);
      if (maps.emissive) {
        materialParams.emissiveMap = new THREE.CanvasTexture(maps.emissive);
        materialParams.emissive = new THREE.Color(0xffffff);
      }

      // Fix color space for diffuse
      if (materialParams.map)
        materialParams.map.colorSpace = THREE.SRGBColorSpace;
      if (materialParams.emissiveMap)
        materialParams.emissiveMap.colorSpace = THREE.SRGBColorSpace;

      this.generatedMaterials[id] = new THREE.MeshStandardMaterial(
        materialParams
      );
    }
  }

  renderGrid(grid) {
    // Clear previous meshes
    if (this.meshGroup) {
      this.scene.remove(this.meshGroup);
    }
    this.meshGroup = new THREE.Group();
    this.scene.add(this.meshGroup);

    // Geometry reused
    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const geometrySlab = new THREE.BoxGeometry(1, 0.9, 1); // 0.9 Thickness for solid ramps

    const materialWall = new THREE.MeshStandardMaterial({ color: 0x888888 }); // Gray walls
    const materialFloor = new THREE.MeshStandardMaterial({ color: 0x44aa44 }); // Green floors
    const materialCover = new THREE.MeshStandardMaterial({ color: 0xaa4444 }); // Red cover
    const materialDetail = new THREE.MeshStandardMaterial({ color: 0xaa44aa }); // Purple details
    const materialCrystal = new THREE.MeshStandardMaterial({ color: 0x00ffff }); // Cyan crystals
    const materialStructure = new THREE.MeshStandardMaterial({
      color: 0x4444aa,
    }); // Blue structure
    const materialBridge = new THREE.MeshStandardMaterial({
      color: 0x00ffff,
      transparent: true,
      opacity: 0.6,
    });
    const materialTeleporter = new THREE.MeshStandardMaterial({
      color: 0xff00ff,
      emissive: 0xff00ff,
      emissiveIntensity: 1.0,
    });

    const materialMud = new THREE.MeshStandardMaterial({ color: 0x5c4033 }); // Dark Brown
    const materialSandbag = new THREE.MeshStandardMaterial({ color: 0xc2b280 }); // Sand/Tan

    const materialObsidian = new THREE.MeshStandardMaterial({
      color: 0x111111,
      roughness: 0.1,
      metalness: 0.8,
    });
    const materialCorruption = new THREE.MeshStandardMaterial({
      color: 0xaa00ff,
      emissive: 0xaa00ff,
      emissiveIntensity: 0.5,
    });

    const materialGlowingPillar = new THREE.MeshStandardMaterial({
      color: 0xaa44ff,
      emissive: 0xaa44ff,
      emissiveIntensity: 1.0,
    });

    const useTextures = this.textureToggle.checked;

    // We'll just create simple meshes for now instead of InstancedMesh for simplicity of debugging diverse IDs
    // Optimization: Use InstancedMesh later if too slow.

    for (let x = 0; x < grid.size.x; x++) {
      for (let y = 0; y < grid.size.y; y++) {
        for (let z = 0; z < grid.size.z; z++) {
          const id = grid.getCell(x, y, z);
          if (id !== 0) {
            let mat;

            if (useTextures && this.generatedMaterials[id]) {
              mat = this.generatedMaterials[id];
            } else {
              // Heuristic for coloring based on IDs in CaveGenerator
              if (id === 20) mat = materialBridge;
              else if (id === 22) mat = materialTeleporter;
              else if (id >= 200 && id < 300) mat = materialFloor;
              else if (id === 10) mat = materialCover; // Cover
              else if (id === 15) mat = materialCrystal; // Crystal Scatter
              else if (id === 12) mat = materialSandbag; // Sandbags
              else if (id === 11) mat = materialDetail; // Wall Detail
              else if (id === 50 || id === 52) mat = materialObsidian;
              else if (id === 51 || id === 55) mat = materialCorruption;
              else if (id === 53) mat = materialGlowingPillar;
              else if (id >= 108 && id <= 109)
                mat = materialDetail; // Veined Walls (Debug highlight)
              else if (id >= 120 && id < 200)
                mat = materialMud; // Trench Walls (Muddy)
              else if (id >= 100 && id < 200) mat = materialWall;
              else mat = materialStructure;
            }

            let useGeo = geometry;
            let posY = y + 0.5;

            // ID 20 = Bridges (Stairs visually)
            if (id === 20) {
              useGeo = geometrySlab;
              posY = y + 0.45; // Bottom-ish of cell (size 0.9, center at 0.45)
            }

            const mesh = new THREE.Mesh(useGeo, mat);
            mesh.position.set(x, posY, z);
            this.meshGroup.add(mesh);
          }
        }
      }
    }

    // Center camera
    this.controls.target.set(grid.size.x / 2, 0, grid.size.z / 2);
  }

  renderZones(spawnZones) {
    if (this.zoneGroup) {
      this.scene.remove(this.zoneGroup);
    }
    this.zoneGroup = new THREE.Group();
    this.scene.add(this.zoneGroup);

    // Player Zones (Blue)
    const playerGeo = new THREE.BoxGeometry(0.8, 1, 0.8);
    const playerMat = new THREE.MeshBasicMaterial({
      color: 0x0000ff,
      transparent: true,
      opacity: 0.3,
    });

    // Enemy Zones (Red)
    const enemyMat = new THREE.MeshBasicMaterial({
      color: 0xff0000,
      transparent: true,
      opacity: 0.3,
    });

    if (spawnZones.player) {
      spawnZones.player.forEach((pos) => {
        const mesh = new THREE.Mesh(playerGeo, playerMat);
        mesh.position.set(pos.x, pos.y + 0.5, pos.z);
        this.zoneGroup.add(mesh);
      });
    }

    if (spawnZones.enemy) {
      spawnZones.enemy.forEach((pos) => {
        const mesh = new THREE.Mesh(playerGeo, enemyMat);
        mesh.position.set(pos.x, pos.y + 0.5, pos.z);
        this.zoneGroup.add(mesh);
      });
    }

    this.zoneGroup.visible = this.zoneToggle.checked;
  }

  onWindowResize() {
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(window.innerWidth, window.innerHeight);
  }

  animate() {
    requestAnimationFrame(() => this.animate());
    this.controls.update();
    this.renderer.render(this.scene, this.camera);
  }
}

new MapVisualizer();
