class Viewer3D {
  constructor(containerId, emptyStateId) {
    this.container = document.getElementById(containerId);
    this.emptyState = document.getElementById(emptyStateId);
    
    this.scene = null;
    this.camera = null;
    this.renderer = null;
    this.controls = null;
    this.raycaster = null;
    
    this.rooms = [];
    this.roomMeshes = [];
    this.activeRoomId = null;
    this.onSelectRoom = null;

    this.isInitialized = false;
  }

  // Inicializar o motor Three.js
  initThree() {
    if (this.isInitialized) return;

    const width = this.container.clientWidth || 800;
    const height = this.container.clientHeight || 500;

    // 1. Criar Cena
    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color(0x0a0b10); // Combina com o background do CSS (--bg-primary)
    
    // Adicionar neblina sutil para profundidade estética
    this.scene.fog = new THREE.FogExp2(0x0a0b10, 0.015);

    // 2. Criar Câmera
    this.camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
    
    // 3. Criar Renderizador WebGL
    this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
    this.renderer.setSize(width, height);
    this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    
    // Injetar canvas no DOM
    this.container.appendChild(this.renderer.domElement);

    // 4. OrbitControls para rotação, pan e zoom
    this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
    this.controls.enableDamping = true;
    this.controls.dampingFactor = 0.05;
    this.controls.maxPolarAngle = Math.PI / 2 - 0.05; // Evita navegar abaixo do chão
    this.controls.minDistance = 10;
    this.controls.maxDistance = 150;

    // 5. Adicionar Iluminação Realista
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
    this.scene.add(ambientLight);

    const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
    dirLight.position.set(40, 60, 20);
    dirLight.castShadow = true;
    dirLight.shadow.mapSize.width = 1024;
    dirLight.shadow.mapSize.height = 1024;
    dirLight.shadow.camera.near = 0.5;
    dirLight.shadow.camera.far = 200;
    const d = 50;
    dirLight.shadow.camera.left = -d;
    dirLight.shadow.camera.right = d;
    dirLight.shadow.camera.top = d;
    dirLight.shadow.camera.bottom = -d;
    this.scene.add(dirLight);

    // Luz de preenchimento azulada para atmosfera cyberpunk/premium
    const fillLight = new THREE.DirectionalLight(0x6366f1, 0.4);
    fillLight.position.set(-40, 20, -20);
    this.scene.add(fillLight);

    // 6. Adicionar Grid e Chão
    const gridHelper = new THREE.GridHelper(120, 40, 0x6366f1, 0x1b1e32);
    gridHelper.position.y = -0.05;
    this.scene.add(gridHelper);

    // Plano de chão sutil que recebe sombras
    const floorGeo = new THREE.PlaneGeometry(200, 200);
    const floorMat = new THREE.MeshStandardMaterial({
      color: 0x0c0d14,
      roughness: 0.9,
      metalness: 0.1
    });
    const floor = new THREE.Mesh(floorGeo, floorMat);
    floor.rotation.x = -Math.PI / 2;
    floor.receiveShadow = true;
    this.scene.add(floor);

    // 7. Configurar Câmera Inicial
    this.resetCamera();

    // 8. Inicializar Raycasting para cliques nas salas
    this.raycaster = new THREE.Raycaster();
    this.setupInteraction();

    // 9. Loop de Animação
    const animate = () => {
      requestAnimationFrame(animate);
      if (this.controls) this.controls.update();
      if (this.renderer && this.scene && this.camera) {
        this.renderer.render(this.scene, this.camera);
      }
    };
    animate();

    this.isInitialized = true;
  }

  resetCamera() {
    if (!this.camera || !this.controls) return;
    this.camera.position.set(0, 50, 70);
    this.controls.target.set(0, 0, 0);
    this.controls.update();
  }

  // Configurar clique para selecionar sala no 3D
  setupInteraction() {
    this.renderer.domElement.addEventListener('click', (e) => {
      // Ignorar cliques que fazem parte de um arrastar de câmera (OrbitControls)
      if (this.controls.state !== -1) return;

      const rect = this.renderer.domElement.getBoundingClientRect();
      const mouse = new THREE.Vector2();
      mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;

      this.raycaster.setFromCamera(mouse, this.camera);
      const intersects = this.raycaster.intersectObjects(this.roomMeshes);

      if (intersects.length > 0) {
        const clickedMesh = intersects[0].object;
        const roomId = clickedMesh.userData.roomId;
        if (roomId && this.onSelectRoom) {
          // Disparar o callback global no app.js
          this.onSelectRoom(roomId);
        }
      }
    });

    // Efeito de hover (mudar cursor para pointer ao passar sobre salas)
    this.renderer.domElement.addEventListener('mousemove', (e) => {
      const rect = this.renderer.domElement.getBoundingClientRect();
      const mouse = new THREE.Vector2();
      mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;

      this.raycaster.setFromCamera(mouse, this.camera);
      const intersects = this.raycaster.intersectObjects(this.roomMeshes);
      
      this.renderer.domElement.style.cursor = intersects.length > 0 ? 'pointer' : 'default';
    });
  }

  // Atualizar a cena com base na lista de salas
  updateScene(rooms) {
    this.rooms = rooms;

    // Verificar se há salas com polígonos mapeados
    const hasMappedRooms = rooms.some(r => r.polygon && r.polygon.length >= 3);

    if (!hasMappedRooms) {
      this.emptyState.style.display = 'flex';
      if (this.renderer) this.renderer.domElement.style.display = 'none';
      return;
    }

    // Inicializar o Three.js se ainda não estiver feito
    this.initThree();
    
    this.emptyState.style.display = 'none';
    this.renderer.domElement.style.display = 'block';

    // Limpar malhas existentes
    this.roomMeshes.forEach(mesh => this.scene.remove(mesh));
    this.roomMeshes = [];

    // Fator de escala para converter a escala 0-1 em coordenadas 3D (ex: 80x80 unidades)
    const scale = 80;
    const offset = scale / 2;

    rooms.forEach(room => {
      if (!room.polygon || room.polygon.length < 3) return;

      // 1. Criar o formato 2D no plano
      const shape = new THREE.Shape();
      
      // Mapeia o ponto inicial. Nota: Invertemos o Y no Three.js para alinhar com o SVG
      const p0 = room.polygon[0];
      shape.moveTo(p0.x * scale - offset, -(p0.y * scale - offset));
      
      for (let i = 1; i < room.polygon.length; i++) {
        const p = room.polygon[i];
        shape.lineTo(p.x * scale - offset, -(p.y * scale - offset));
      }
      shape.closePath();

      // 2. Configurar extrusão (maquete volumétrica)
      // Usar a altura do ambiente se cadastrado, senão usar pé-direito padrão de 3.0m (escala proporcional)
      const roomHeight = room.dimensions && room.dimensions.height ? room.dimensions.height : 3.0;
      const extrudeHeight = roomHeight * 2; // Multiplicador visual para a altura ficar agradável

      const extrudeSettings = {
        steps: 1,
        depth: extrudeHeight,
        bevelEnabled: true,
        bevelThickness: 0.1,
        bevelSize: 0.1,
        bevelSegments: 2
      };

      const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);

      // 3. Configurar materiais baseados no status da sala
      const color = this.getStatusColor(room.status);
      const isSelected = room.id === this.activeRoomId;

      const material = new THREE.MeshStandardMaterial({
        color: color,
        roughness: 0.3,
        metalness: 0.2,
        transparent: true,
        opacity: isSelected ? 0.95 : 0.65,
        emissive: color,
        emissiveIntensity: isSelected ? 0.25 : 0.05
      });

      // 4. Instanciar a malha
      const mesh = new THREE.Mesh(geometry, material);
      
      // Rotacionar em -90 graus no eixo X para deitar a planta baixa no chão
      mesh.rotation.x = -Math.PI / 2;
      mesh.castShadow = true;
      mesh.receiveShadow = true;

      // Armazenar ID da sala para raycasting
      mesh.userData = { roomId: room.id };

      this.scene.add(mesh);
      this.roomMeshes.push(mesh);
    });

    // Ajustar foco da câmera para o centro médio dos polígonos
    this.adjustCameraFocus();
  }

  // Retorna a cor do material com base no status da preparação
  getStatusColor(status) {
    switch (status) {
      case 'Pronto':
        return 0x10b981; // Verde Emerald
      case 'Em Preparação':
        return 0xf59e0b; // Laranja/Amber
      case 'Manutenção':
        return 0xef4444; // Vermelho
      default:
        return 0x6366f1; // Indigo
    }
  }

  // Destaque visual na sala selecionada
  highlightRoom(roomId) {
    this.activeRoomId = roomId;

    this.roomMeshes.forEach(mesh => {
      const isSelected = mesh.userData.roomId === roomId;
      const room = this.rooms.find(r => r.id === mesh.userData.roomId);
      const baseColor = this.getStatusColor(room ? room.status : '');

      if (isSelected) {
        mesh.material.opacity = 0.95;
        mesh.material.emissiveIntensity = 0.35;
        mesh.material.emissive.setHex(0xffffff); // Brilho branco na sala selecionada
      } else {
        mesh.material.opacity = 0.65;
        mesh.material.emissiveIntensity = 0.05;
        mesh.material.emissive.setHex(baseColor);
      }
    });
  }

  // Centralizar câmera nos polígonos existentes
  adjustCameraFocus() {
    if (this.roomMeshes.length === 0) return;

    // Calcular caixa envolvente de todas as malhas das salas
    const bbox = new THREE.Box3();
    this.roomMeshes.forEach(mesh => {
      mesh.geometry.computeBoundingBox();
      const tempBox = mesh.geometry.boundingBox.clone();
      tempBox.applyMatrix4(mesh.matrixWorld);
      bbox.union(tempBox);
    });

    const center = new THREE.Vector3();
    bbox.getCenter(center);
    
    // Mover o centro do controle OrbitControls para o centro da maquete
    this.controls.target.copy(center);
    this.controls.update();
  }

  // Tratar redimensionamento
  handleResize() {
    if (!this.renderer || !this.container || !this.camera) return;
    
    const width = this.container.clientWidth;
    const height = this.container.clientHeight;

    this.camera.aspect = width / height;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(width, height);
  }
}
