/* ========================================================================
   IMPORTAÇÕES
   ======================================================================== */
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';


/* ========================================================================
   VARIÁVEIS GLOBAIS E CONFIGURAÇÕES INICIAIS
   ======================================================================== */
let mixer, caixaMixer;
let moverFrascoAction, abrirTampaAction, openBoxAction, pingarRemedioAction, animGotaCaindoUmAction;
let action;
let animationPlayingForwardFrasco = true;
let animationPlayingForwardBox = true;
let animationPlayingForwardTampa = true;

// Objetos da cena
let caixa, gota, poca;

// Configurações da poça
let gotaInterval = null;
let pocaScaleFactor = 0.2;
const maxPocaScale = 2;

// Hotspot pulsante
let pulseClock = new THREE.Clock();
let pulseSpeed = 3;       // Velocidade do pulso
let baseScale = 0.07;     // Escala inicial
let pulseAmplitude = 0.01; // Intensidade da variação

// labels
let label;
let labelApresentacao;
let labelPadronizacao;
let labelComposicao;
let labelGotejador;
let labelControleDose;
let labelPureza;


/* ========================================================================
   LOADING SCREEN (Tela de carregamento)
   ======================================================================== */
const loadingManager = new THREE.LoadingManager();
let isLoaded = false;

loadingManager.onLoad = () => {
  isLoaded = true;
  const loadingScreen = document.getElementById('loading-screen');

  // Ativa o fade-out
  loadingScreen.classList.add('fade-out');

  // Remove após o fade
  setTimeout(() => {
    loadingScreen.style.display = 'none';
  }, 1000);
};





/* ========================================================================
   CENA E RENDERIZADOR
   ======================================================================== */
const scene = new THREE.Scene();

// Fundo com textura gradiente
const loaderTexture = new THREE.TextureLoader();
loaderTexture.load('assets/img/gradiente.jpg', (texture) => {
  scene.background = texture;
});

// Câmera
const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1.2, 2.5);

// Renderizador
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0xf0f0f0);
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);

// Label Renderer
const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0';
labelRenderer.domElement.style.pointerEvents = 'none';
document.body.appendChild(labelRenderer.domElement);

/* ========================================================================
   ILUMINAÇÃO
   ======================================================================== */
// Luz ambiente tipo HDRI
const light = new THREE.HemisphereLight(0xfff5c0, 0xffcc88, 1.5);
scene.add(light);

// Luz direcional principal (sombras)
const dirLight = new THREE.DirectionalLight(0xffffff, 2.5);
dirLight.position.set(5, 10, 5);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 4096;
dirLight.shadow.mapSize.height = 4096;
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
dirLight.shadow.camera.near = 0.5;
dirLight.shadow.camera.far = 50;
dirLight.shadow.bias = -0.0001;
dirLight.shadow.normalBias = 0.02;
scene.add(dirLight);

// Luzes de contorno (rim lights)
const rimLight = new THREE.DirectionalLight(0xadd8e6, 2.5);
rimLight.position.set(-5, 5, -5);
scene.add(rimLight);

const rimLightTwo = new THREE.DirectionalLight(0xadd8e6, 1);
rimLightTwo.position.set(0, 0, 5);
scene.add(rimLightTwo);

/* ========================================================================
   CHÃO E PLANO DE SOMBRA
   ======================================================================== */
// Plano invisível que recebe sombras
const groundGeo = new THREE.PlaneGeometry(200, 200);
const groundMat = new THREE.ShadowMaterial({ opacity: 0.25 });
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.position.y = 0.530;
ground.receiveShadow = true;
scene.add(ground);

// Círculo verde suave no chão
const circleGeo = new THREE.CircleGeometry(1, 64);
circleGeo.rotateX(-Math.PI / 2);

function createRadialAlphaTexture(color = 'rgba(124, 172, 82, 1)') {
  const size = 512;
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = canvas.height = size;

  const gradient = ctx.createRadialGradient(size/2, size/2, 0, size/2, size/2, size/2);
  gradient.addColorStop(0, color);
  gradient.addColorStop(1, 'rgba(124, 172, 82,0)');

  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, size, size);

  const texture = new THREE.CanvasTexture(canvas);
  texture.encoding = THREE.sRGBEncoding;
  texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
  return texture;
}

const circleMat = new THREE.MeshBasicMaterial({
  map: createRadialAlphaTexture(),
  transparent: true,
  opacity: 1,
  depthWrite: false,
  side: THREE.DoubleSide
});

const circleMesh = new THREE.Mesh(circleGeo, circleMat);
circleMesh.scale.set(2, 2, 2);
circleMesh.position.y = 0.531;
scene.add(circleMesh);

/* ========================================================================
   CONTROLES DE ÓRBITA (OrbitControls)
   ======================================================================== */
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minPolarAngle = Math.PI / 4;
controls.maxPolarAngle = Math.PI / 2.1;
controls.autoRotate = true;
controls.autoRotateSpeed = 1.0;
controls.minDistance = 1.5;
controls.maxDistance = 3.5;
controls.zoomSpeed = 0.8;
controls.target.set(0, 1.1, 0);
controls.enablePan = false;
controls.update();

// Auto-rotação após inatividade
let autoRotateTimeout;
let userInteracting = false;

function resetAutoRotateTimer() {
  clearTimeout(autoRotateTimeout);
  controls.autoRotate = false;
  userInteracting = true;

  autoRotateTimeout = setTimeout(() => {
    controls.autoRotate = true;
    userInteracting = false;
  }, 5000);
}

['mousedown','mousemove','wheel','touchstart','touchmove']
  .forEach(event => window.addEventListener(event, resetAutoRotateTimer, false));

/* ========================================================================
   LOADER DE MODELOS GLTF
   ======================================================================== */
const loader = new GLTFLoader(loadingManager);

// 💊 Embalagem
loader.load(
  'assets/models/embalagem.glb',
  function (gltf) {
    caixa = gltf.scene;
    caixa.position.set(0, 0.53, 0);

    caixa.traverse((child) => {
      if (child.isMesh) {
        child.castShadow = true;
        child.receiveShadow = true;

        const material = child.material;

        // Se o material tiver uma textura
        if (material.map) {
          //material.map.minFilter = THREE.NearestFilter;
          //material.map.magFilter = THREE.NearestFilter;
          material.map.generateMipmaps = true;
          material.map.encoding = THREE.sRGBEncoding;
          material.map.needsUpdate = true; 
          material.map.minFilter = THREE.LinearMipMapLinearFilter;
          material.map.magFilter = THREE.LinearFilter;
          material.map.anisotropy = renderer.capabilities.getMaxAnisotropy();
          material.map.encoding = THREE.sRGBEncoding;
          material.map.needsUpdate = true;
        }
      }
    });

    scene.add(caixa);

    // 🎞️ Se houver animações no GLTF
    if (gltf.animations && gltf.animations.length > 0) {
      caixaMixer = new THREE.AnimationMixer(caixa);
      const clip = THREE.AnimationClip.findByName(gltf.animations, 'OpenBox');
      if (clip) {
        openBoxAction = caixaMixer.clipAction(clip);
        openBoxAction.clampWhenFinished = true;
        openBoxAction.setLoop(THREE.LoopOnce);
      }
    }
  },
  undefined,
  function (error) {
    console.error('Erro ao carregar embalagem.glb:', error);
  }
);

// 💊 Frasco do remédio com vidro fake
loader.load(
  'assets/models/remedio.glb',
  function (gltf) {
    const remedio = gltf.scene;
    remedio.position.set(0, 0.53, 0);

    const frasco = remedio.getObjectByName("Frasco");
    frasco.traverse((child) => {
      if (child.isMesh && child.material.name === "Vidro") {
        child.material = new THREE.MeshPhysicalMaterial({
          color: 0x54280b, // tom marrom acobreado
            roughness: 0,
            metalness: 0,
            transparent: true,
            opacity: 0.9,
            transmission: 0.8, // permite passagem parcial de luz
            thickness: 0.9,    // simula profundidade do vidro
            ior: 1.3,          // índice de refração parecido com vidro real
            clearcoat: 1,
        });
          child.castShadow = true;
          child.receiveShadow = true;

            const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              material.map.minFilter = THREE.NearestFilter;
              material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = false;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; // importante!
            }
        }
        if (child.isMesh && child.material.name === "Liquido") {
        child.material = new THREE.MeshStandardMaterial({
            color: 0x2E1606, // tom marrom acobreado
            roughness: 0.2,
            metalness: 0.3,
          });
          child.castShadow = true;
          child.receiveShadow = true;

            const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              material.map.minFilter = THREE.NearestFilter;
              material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = false;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; // importante!
            }
        }
        if (child.isMesh && child.material.name === "ContaGotasTexture") {
        child.material = new THREE.MeshPhysicalMaterial({
            color: 0xe5e5e5,
            roughness: 0.2,
            metalness: 0,
            transparent: true,
            opacity: 0.65,
            transmission: 0,
            thickness: 0.8,
            depthWrite: false
          });
          child.castShadow = true;
          child.receiveShadow = true;

            const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              material.map.minFilter = THREE.NearestFilter;
              material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = false;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; // importante!
            }
        }
      });

    gota = remedio.getObjectByName("Gota");

    [gota].forEach((obj) => {
      if (obj) {
        obj.traverse((child) => {
          if (child.isMesh) {
            child.material = new THREE.MeshPhysicalMaterial({
              color: 0x292c0a,
                roughness: .5,
                metalness: 0,
                transparent: true,
                opacity: 0.85,
                transmission: 0.5, // permite passagem parcial de luz
                thickness: 0.5,    // simula profundidade
                clearcoat: 1,
            });
            child.visible = false;
            child.castShadow = true;
            child.receiveShadow = true;

            const material = child.material;
            if (material.map) {
              material.map.generateMipmaps = true;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true;
              material.map.minFilter = THREE.LinearMipMapLinearFilter;
              material.map.magFilter = THREE.LinearFilter;
              material.map.anisotropy = renderer.capabilities.getMaxAnisotropy();
            }
          }
        });
      }
    });

    const sealer = remedio.getObjectByName("Sealer");

    const cover = remedio.getObjectByName("Cover");
    cover.traverse((child) => {
      if (child.isMesh) {
        child.castShadow = true;
        child.receiveShadow = true;

           const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              //material.map.minFilter = THREE.NearestFilter;
              //material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = true;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; 
              material.map.minFilter = THREE.LinearMipMapLinearFilter;
              material.map.magFilter = THREE.LinearFilter;
              material.map.anisotropy = renderer.capabilities.getMaxAnisotropy();
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true;
              material.map.roughness = 0.4;
              material.map.metalness = 0.3;
            }
      }
    });

    const plasticoBranco = new THREE.MeshPhysicalMaterial({
      color: 0xdbdbdb,
      roughness: 0.9,
      metalness: 0,
      transparent: true,
      opacity: 0.8,
      transmission: 0.7,
      thickness: 0.1,
      depthWrite: false
    });

    [sealer].forEach((obj) => {
      if (obj) {
        obj.traverse((child) => {
          if (child.isMesh) {
            child.material = plasticoBranco;
            child.castShadow = true;
            child.receiveShadow = true;

               const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              //material.map.minFilter = THREE.NearestFilter;
              //material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = true;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; 
              material.map.minFilter = THREE.LinearMipMapLinearFilter;
              material.map.magFilter = THREE.LinearFilter;
              material.map.anisotropy = renderer.capabilities.getMaxAnisotropy();
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true;
            }
          }
        });
      }
    });

    remedio.traverse((child) => {
      if (child.isMesh) {
        child.castShadow = true;
        child.receiveShadow = true;

           const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              //material.map.minFilter = THREE.NearestFilter;
              //material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = true;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; 
              material.map.minFilter = THREE.LinearMipMapLinearFilter;
              material.map.magFilter = THREE.LinearFilter;
              material.map.anisotropy = renderer.capabilities.getMaxAnisotropy();
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true;
            }
      }
    });
    

    scene.add(remedio);

    // Animações
    if (gltf.animations && gltf.animations.length > 0) {
      mixer = new THREE.AnimationMixer(remedio);

      // Animação de mover o frasco
      const animMover = THREE.AnimationClip.findByName(gltf.animations, 'RetirarRemedio');
      if (animMover) {
        moverFrascoAction = mixer.clipAction(animMover);
        moverFrascoAction.clampWhenFinished = true;
        moverFrascoAction.setLoop(THREE.LoopOnce);
      }

      // Animação de abrir tampa
      const animAbrir = THREE.AnimationClip.findByName(gltf.animations, 'AbrirTampa');
      if (animAbrir) {
        abrirTampaAction = mixer.clipAction(animAbrir);
        abrirTampaAction.clampWhenFinished = true;
        abrirTampaAction.setLoop(THREE.LoopOnce);
      }

      // Animação de pingar Remedio
      const animPingar = THREE.AnimationClip.findByName(gltf.animations, 'PingarRemedio');
      if (animPingar) {
        pingarRemedioAction = mixer.clipAction(animPingar);
        pingarRemedioAction.clampWhenFinished = true;
        pingarRemedioAction.setLoop(THREE.LoopOnce);
      }

      // Animação de cair Gota 1
      const animGotaCaindoUm = THREE.AnimationClip.findByName(gltf.animations, 'GotaCaindo');
      if (animGotaCaindoUm) {
        animGotaCaindoUmAction = mixer.clipAction(animGotaCaindoUm);
        animGotaCaindoUmAction.clampWhenFinished = true;
        animGotaCaindoUmAction.setLoop(THREE.LoopOnce);
      }

    }
  },
  undefined,
  function (error) {
    console.error('Erro ao carregar remedio.glb:', error);
  }
);

// 💊 poca
loader.load(
  'assets/models/poca.glb',
  function (gltf) {
    poca = gltf.scene;
    poca.position.set(0.23, 0.53, 0.63);
    poca.scale.set(1, 1, 1);
    poca.visible = false

    // 🔍 Objeto 1: pocalarga
    const pocalarga = poca.getObjectByName("pocalarga");
    pocalarga.traverse((child) => {
      if (child.isMesh && child.material.name === "claro") {
        child.material = new THREE.MeshPhysicalMaterial({
          color: 0x6a6f33,
            roughness: .5,
            metalness: 0,
            transparent: true,
            opacity: 0.9,
            transmission: 0.9, // permite passagem parcial de luz
            thickness: 0.9,    // simula profundidade
            clearcoat: 1,
        });
          child.castShadow = true;
          child.receiveShadow = true;

            const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              material.map.minFilter = THREE.NearestFilter;
              material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = false;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; // importante!
            }
        }
        if (child.isMesh && child.material.name === "escuro") {
        child.material = new THREE.MeshStandardMaterial({
            color: 0x242705, // tom marrom acobreado
            roughness: 1,
            metalness: 0.3,
          });
          child.castShadow = true;
          child.receiveShadow = true;

            const material = child.material;
            // Se o material tiver uma textura
            if (material.map) {
              material.map.minFilter = THREE.NearestFilter;
              material.map.magFilter = THREE.NearestFilter;
              material.map.generateMipmaps = false;
              material.map.encoding = THREE.sRGBEncoding;
              material.map.needsUpdate = true; // importante!
            }
        }
      });
      

    scene.add(poca);
  },
  undefined,
  function (error) {
    console.error('Erro ao carregar poca.glb:', error);
  }
);

function expandirPocaSuavemente() {
  if (!poca || pocaScaleFactor >= maxPocaScale) return;

  pocaScaleFactor += 1;

  const targetScale = pocaScaleFactor;
  const startScale = poca.scale.x;
  const duration = 1000; // 1 segundo
  const startTime = performance.now();

  function animateScale(time) {
    const elapsed = time - startTime;
    const progress = Math.min(elapsed / duration, 1);
    const newScale = startScale + (targetScale - startScale) * progress;

    poca.scale.set(newScale, newScale, newScale);
    //poca.position.set(0, 0.53, 0);

    if (progress < 1) {
      requestAnimationFrame(animateScale);
    }
  }

  requestAnimationFrame(animateScale);
}

/* ========================================================================
   HOTSPOTS (interação clicável na cena)
   ======================================================================== */
const spriteMap = new THREE.TextureLoader().load('./assets/img/dot2.svg'); // Ícone do hotspot
const spriteMaterial = new THREE.SpriteMaterial({ map: spriteMap, transparent: true });
const hotspotSprite = new THREE.Sprite(spriteMaterial);
hotspotSprite.scale.set(0.07, 0.07, 1);
hotspotSprite.position.set(-0, 1.525, 0.220);
scene.add(hotspotSprite);

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

// Lista de posições que o hotspot assume ao longo da animação
const hotspotPositions = [
  new THREE.Vector3(-0, 1.515, 0.220), // OpenBox
  new THREE.Vector3(0.35, 1.415, 0.520), // moverFrasco
  new THREE.Vector3(0.28, 1.15, 0.520), // abrirTampa
];

// Atualiza posição do hotspot conforme o índice
function updateHotspotPosition(index) {
  const newPos = hotspotPositions[index];
  if (newPos) hotspotSprite.position.copy(newPos);
}

// Clique no hotspot
window.addEventListener('click', (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mouse, camera);

  const intersects = raycaster.intersectObjects(scene.children, true);
  if (intersects.length > 0) {
    const primeiroObjeto = intersects[0].object;
    if (primeiroObjeto === hotspotSprite) {
      if (hotspotIndex > 3) return;
      executarPasso(hotspotIndex);
    }
  }
});


/* ========================================================================
   ANOTAÇÕES INFORMATIVAS (Labels flutuantes)
   ======================================================================== */

function createLabel(text, position, align = 'center', id = '') {
  const outer = document.createElement('div');
  outer.className = 'label hidden';
  if (id) outer.id = id;
  outer.style.position = 'relative';
  outer.style.pointerEvents = 'none';

  const inner = document.createElement('div');
  inner.className = 'label-content';
  inner.textContent = text;
  inner.style.position = 'absolute';
  inner.style.top = '50%';
  inner.style.transform = 'translateY(-50%)';

  // Estilo visual
  inner.style.padding = '6px 10px';
  inner.style.background = 'rgba(255,255,255,0.85)';
  inner.style.borderRadius = '6px';
  inner.style.fontSize = '16px';
  inner.style.color = '#333';
  inner.style.boxShadow = '0 2px 6px rgba(0,0,0,0.2)';
  inner.style.whiteSpace = 'normal';
  inner.style.wordBreak = 'break-word';
  inner.style.display = 'inline-block';
  inner.style.width = '200px';
  inner.style.textAlign = 'center';

  // 🔀 Alinhamento horizontal
  switch (align) {
    case 'left':
      inner.style.left = '0';
      break;

    case 'right':
      inner.style.left = '0';
      inner.style.transform += ' translateX(-100%)';
      break;

    case 'center':
    default:
      inner.style.left = '50%';
      inner.style.transform += ' translateX(-50%)';
      break;
  }

  // 🔻 Triângulo indicador dentro do inner
  const arrow = document.createElement('div');
  arrow.style.position = 'absolute';
  arrow.style.width = '0';
  arrow.style.height = '0';
  arrow.style.border = '6px solid transparent';

  switch (align) {
    case 'left':
      arrow.style.left = '-12px';
      arrow.style.top = '50%';
      arrow.style.transform = 'translateY(-50%)';
      arrow.style.borderRightColor = 'rgba(255,255,255,0.85)';
      break;

    case 'right':
      arrow.style.right = '-12px';
      arrow.style.top = '50%';
      arrow.style.transform = 'translateY(-50%)';
      arrow.style.borderLeftColor = 'rgba(255,255,255,0.85)';
      break;

    case 'center':
    default:
      arrow.style.bottom = '-12px';
      arrow.style.left = '50%';
      arrow.style.transform = 'translateX(-50%)';
      arrow.style.borderTopColor = 'rgba(255,255,255,0.85)';
      break;
  }

  inner.appendChild(arrow);
  outer.appendChild(inner);

  const label = new CSS2DObject(outer);
  label.position.copy(position);
  return label;
}

// 🎯 Labels informativos nomeados
labelApresentacao = createLabel('Apresentações com 10ml e 30ml', new THREE.Vector3(-0.266, 0.601, 0.2), 'right', 'label-apresentacao');
labelPadronizacao = createLabel('Padronização lote a lote', new THREE.Vector3(0.266, 1.2, 0.2), 'left', 'label-padronizacao');
labelComposicao = createLabel('1ml = 37 gotas = 50mg de CBD e < 2mg de THC', new THREE.Vector3(0.585, 1.255, 0.455), 'left', 'label-composicao');
labelGotejador = createLabel('Gotejador que evita contato com oxigênio', new THREE.Vector3(0.555, 1.265, 0.510), 'left', 'label-gotejador');
labelControleDose = createLabel('Controle preciso da dose administrada', new THREE.Vector3(0.355, 0.895, 0.550), 'left', 'label-controle-dose');
labelPureza = createLabel('Grau farmacêutico do óleo e ativos', new THREE.Vector3(0.090, 0.565, 0.580), 'right', 'label-pureza');


// Adiciona todos à cena
scene.add(labelApresentacao);
scene.add(labelPadronizacao);
scene.add(labelComposicao);
scene.add(labelGotejador);
scene.add(labelControleDose);
scene.add(labelPureza);

const todosLabels = [
  labelApresentacao,
  labelPadronizacao,
  labelComposicao,
  labelGotejador,
  labelControleDose,
  labelPureza
];

[labelApresentacao, labelPadronizacao, labelComposicao, labelGotejador, labelControleDose, labelPureza].forEach(label => {
  label.element.style.display = 'none';
});

function mostrarLabels(...labelsParaMostrar) {
  const todos = [
    labelApresentacao,
    labelPadronizacao,
    labelComposicao,
    labelGotejador,
    labelControleDose,
    labelPureza
  ];

  todos.forEach(label => {
    if (label && label.element) {
      label.element.classList.add('hidden');
      label.element.classList.remove('fade-in');
    }
  });

  labelsParaMostrar.forEach(label => {
    if (label && label.element) {
      label.element.classList.remove('hidden');
      label.element.classList.remove('fade-out');
      label.element.style.visibility = 'visible';
      // força reflow para reiniciar a animação
      void label.element.offsetWidth;
      label.element.classList.add('fade-in');
      label.element.style.display = 'block'; // ou 'inline-block'
    }
  });
}

function fadeOutLabels(labels) {
  if (!Array.isArray(labels)) labels = [labels];

  labels.forEach(label => {
    const el = label.element;

    // Garante que está visível
    el.style.opacity = '1';
    el.style.visibility = 'visible';
    el.style.pointerEvents = 'auto';
    el.style.transition = 'opacity 0.5s ease';

    // Força o navegador a registrar o estado inicial
    requestAnimationFrame(() => {
      el.classList.add('fade-out');

      // Esconde após o fade
      setTimeout(() => {
        el.style.visibility = 'hidden';
      }, 500);
    });
  });
}

mostrarLabels(labelApresentacao, labelPadronizacao);


const materialBolinha = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const geometriaBolinha = new THREE.SphereGeometry(0.01, 16, 16);

const paresLabelBolinha = todosLabels.map(label => {
  const bolinha = new THREE.Mesh(geometriaBolinha, materialBolinha);
  bolinha.position.copy(label.position);
  bolinha.material.transparent = true;
  bolinha.material.opacity = 0.0; // invisível
  scene.add(bolinha);

  return { label, bolinha };
});


function verificarObstrucaoBolinha(label, bolinha) {
  raycaster.camera = camera;

  const origem = camera.position.clone();
  const destino = bolinha.position.clone();
  const direcao = new THREE.Vector3().subVectors(destino, origem).normalize();

  raycaster.set(origem, direcao);

  const distancia = origem.distanceTo(destino);

  const objetosValidos = scene.children.filter(obj => obj && obj.matrixWorld);
  const intersects = raycaster.intersectObjects(objetosValidos, true).filter(i => {
    return i.object && i.object !== bolinha && !(i.object instanceof THREE.Sprite);
  });

  const obstrucao = intersects.find(i => i.distance < distancia - 0.01);

  const id = label.element.id;
  const el = document.getElementById(id);

  if (!el) {
    console.warn(`⚠️ Elemento com ID "${id}" não encontrado no DOM`);
    return;
  }

  if (obstrucao) {
    el.style.setProperty('display', 'none', 'important');
  } else {
    el.style.setProperty('display', 'block', 'important');
  }
}


/* ========================================================================
   BOTÃO DE AÇÃO (NextAction)
   ======================================================================== */
let animStep = 0;
const btn = document.getElementById('NextAction');
const icon = btn.querySelector('i');

function trocarParaReset() {
  icon.classList.remove('fa-forward');
  icon.classList.add('fa-arrow-rotate-left');
}

function trocarParaPlay() {
  icon.classList.remove('fa-arrow-rotate-left');
  icon.classList.add('fa-forward');
}

function resetAnimacoes() {
  const canvas = renderer.domElement;
  canvas.classList.add('fade-out');
  clearInterval(gotaInterval);
  gotaInterval = null;
  fadeOutLabels(todosLabels);

  setTimeout(() => {
    // Reset de todas as ações
    openBoxAction.stop();
    moverFrascoAction.stop();
    abrirTampaAction.stop();
    pingarRemedioAction.stop();
    animGotaCaindoUmAction.stop();

    gota.visible = false;
    animStep = 0;
    poca.visible = false;

    mostrarLabels(labelApresentacao, labelPadronizacao);

    trocarParaPlay();
    canvas.classList.remove('fade-out');
    fadeInHotspot();
    controls.target.set(0, 1.1, 0);
    controls.update();
  }, 1000);
}

function reiniciar() {
  hotspotSprite.visible = false;
  hotspotIndex = 0;
  updateHotspotPosition(hotspotIndex);
  resetAnimacoes();
}

function fadeOutHotspot() { hotspotSprite.visible = false; }
function fadeInHotspot() { hotspotSprite.visible = true; }

function desativarBotaoTemporariamente(duracaoMs) {
  const btn = document.getElementById('NextAction');
  btn.disabled = true;
  btn.style.opacity = 0.5;
  btn.style.pointerEvents = 'none';

  setTimeout(() => {
    btn.disabled = false;
    btn.style.opacity = 1;
    btn.style.pointerEvents = 'auto';
  }, duracaoMs);
}

/* ========================================================================
   EXECUÇÃO DAS ETAPAS DA ANIMAÇÃO
   ======================================================================== */
let hotspotIndex = 0;
let targetFinal = new THREE.Vector3(0.3, 0.9, 0.3);
let targetAtual = controls.target.clone();
let transicaoAtiva = false;

function executarPasso(index) {
  fadeOutHotspot();
  switch (index) {
    case 0:
      openBoxAction.reset().play();
      setTimeout(() => moverFrascoAction.reset().play(), 1200);
      const duracao0 = 1200 + moverFrascoAction.getClip().duration * 1000;
      // desaparecer com labels antigos
      fadeOutLabels([labelApresentacao, labelPadronizacao]);
      // ⏱ mostrar labels só depois da animação
      setTimeout(() => {
        mostrarLabels(labelComposicao);
      }, duracao0);
      desativarBotaoTemporariamente(duracao0);
      setTimeout(() => {
        targetFinal.set(0.3, 0.9, 0.3);
        targetAtual.copy(controls.target);
        transicaoAtiva = true;
        hotspotIndex++;
        updateHotspotPosition(hotspotIndex);
        fadeInHotspot();
      }, duracao0);
      break;

    case 1:
      abrirTampaAction.reset().play();
      const duracao1 = abrirTampaAction.getClip().duration * 1000;
      desativarBotaoTemporariamente(duracao1);
      // desaparecer com labels antigos
      fadeOutLabels([labelComposicao]);
      setTimeout(() => {
        mostrarLabels(labelGotejador);
      }, duracao1);
      setTimeout(() => {
        hotspotIndex++;
        updateHotspotPosition(hotspotIndex);
        fadeInHotspot();
      }, duracao1);
      break;

    case 2:
      pingarRemedioAction.reset().play();
      const duracaoGota = animGotaCaindoUmAction.getClip().duration * 1000;
      const tempoTotal = 1600 + duracaoGota;
      desativarBotaoTemporariamente(tempoTotal);
      // desaparecer com labels antigos
      fadeOutLabels([labelGotejador]);
      // ⏱ mostrar labels depois da gota cair
      setTimeout(() => {
        mostrarLabels(labelControleDose, labelPureza);
      }, tempoTotal);

      setTimeout(() => {
        gota.visible = true;
        animGotaCaindoUmAction.reset().play();

        // Após a gota cair, aparece a poça
        setTimeout(() => {
          gota.visible = false;
          if (poca) {
            poca.visible = true;
            poca.traverse((child) => {
              if (child.isMesh) {
                child.material.transparent = true;
                child.material.opacity = 0;
                const startTime = performance.now();
                const duration = 500;
                const targetOpacity = 1;
                function fade(time) {
                  const elapsed = time - startTime;
                  const progress = Math.min(elapsed / duration, 1);
                  child.material.opacity = progress * targetOpacity;
                  if (progress < 1) requestAnimationFrame(fade);
                }
                requestAnimationFrame(fade);
              }
            });
          }
        }, duracaoGota);

        // Loop de gotas recorrentes
        if (!gotaInterval) {
          gotaInterval = setInterval(() => {
            gota.visible = true;
            animGotaCaindoUmAction.reset().play();
            setTimeout(() => {
              gota.visible = false;
              expandirPocaSuavemente();
            }, duracaoGota);
          }, 5000);
        }

        setTimeout(() => {
          trocarParaReset();
          hotspotSprite.visible = false;
          hotspotIndex++;
          updateHotspotPosition(hotspotIndex);
        }, duracaoGota);
      }, 1600);
      break;

    case 3:
      trocarParaReset();
      hotspotSprite.visible = false;
      desativarBotaoTemporariamente(500);

      // Oculta todos os labels
      fadeOutLabels([
        labelApresentacao,
        labelPadronizacao,
        labelComposicao,
        labelGotejador,
        labelControleDose,
        labelPureza
      ]);

      // ⏱ Após o fade, garante que nenhum label reapareça
      setTimeout(() => {
        mostrarLabels(); // nenhum label
      }, 500);
      break;
  }
}

document.getElementById('NextAction').addEventListener('click', () => {
  if (hotspotIndex > 2) {
    reiniciar();
    return;
  }
  executarPasso(hotspotIndex);
});

/* ========================================================================
   EVENTOS DE INTERAÇÃO (mouse, controles)
   ======================================================================== */
const canvas = renderer.domElement;
controls.addEventListener('start', () => canvas.style.cursor = 'grabbing');
controls.addEventListener('end', () => canvas.style.cursor = 'grab');

window.addEventListener('mousemove', (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mouse, camera);

  const intersects = raycaster.intersectObjects(scene.children, true);
  if (intersects.length > 0 && intersects[0].object === hotspotSprite) {
    canvas.style.cursor = 'pointer';
  } else {
    canvas.style.cursor = userInteracting ? 'grabbing' : 'grab';
  }
});

/* ========================================================================
   LOOP DE ANIMAÇÃO (Render loop)
   ======================================================================== */
const clock = new THREE.Clock();

window.addEventListener('resize', () => {
  // Atualiza tamanho do renderer
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

  // Atualiza aspectos da câmera
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  // atualiza posicao das labels
  labelRenderer.setSize(window.innerWidth, window.innerHeight);
});

function toScreenPosition(obj, camera) {
  const vector = new THREE.Vector3();
  obj.updateMatrixWorld();
  vector.setFromMatrixPosition(obj.matrixWorld);
  vector.project(camera);

  const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
  const y = (-vector.y * 0.5 + 0.5) * window.innerHeight;

  return { x, y };
}

function animate() {
  requestAnimationFrame(animate);

  const delta = clock.getDelta();
  if (mixer) mixer.update(delta);
  if (caixaMixer) caixaMixer.update(delta);
  controls.update();

  // Suaviza transição do foco da câmera
  if (transicaoAtiva) {
    targetAtual.lerp(targetFinal, 0.05);
    controls.target.copy(targetAtual);
    controls.update();
    if (targetAtual.distanceTo(targetFinal) < 0.001) {
      controls.target.copy(targetFinal);
      transicaoAtiva = false;
    }
  }

    // Hotspot pulsante
    if (isLoaded && hotspotSprite.visible) {
    const time = pulseClock.getElapsedTime();
    const scale = baseScale + Math.sin(time * pulseSpeed) * pulseAmplitude;
    hotspotSprite.scale.set(scale, scale, 1);
    }

    labelRenderer.render(scene, camera);

    renderer.render(scene, camera);

    // ✅ Só agora os elementos existem no DOM
      paresLabelBolinha.forEach(({ label, bolinha }) => {
        verificarObstrucaoBolinha(label, bolinha);
      });
    }


animate();