// 3D stage — environment + screen, with runtime-tunable params

const StageFactory = (() => {

  const ENV_PRESETS = {
    studio:  { bg: 0x05060a, fog: 0x05060a, floor: 0x1a1c22, accent: 0x4ba3c7, ambient: 0.35, key: 1.4, fillColor: 0xffb070 },
    sunset:  { bg: 0x2a1820, fog: 0x3a2030, floor: 0x2a1c20, accent: 0xff8c5a, ambient: 0.5, key: 1.6, fillColor: 0xff6a40 },
    night:   { bg: 0x02030a, fog: 0x02030a, floor: 0x0a0c14, accent: 0x6080ff, ambient: 0.18, key: 0.8, fillColor: 0x4060c0 },
    neon:    { bg: 0x0a0214, fog: 0x140428, floor: 0x140828, accent: 0xff40c0, ambient: 0.4, key: 1.2, fillColor: 0x40c0ff },
    daylight:{ bg: 0xc0d8e8, fog: 0xc0d8e8, floor: 0xa0a8b0, accent: 0xffffff, ambient: 0.8, key: 1.6, fillColor: 0xfff0d0 },
    forest:  { bg: 0x0a1a14, fog: 0x142820, floor: 0x182418, accent: 0x80c060, ambient: 0.45, key: 1.2, fillColor: 0xfff0a0 },
  };

  function build(scene) {
    const group = new THREE.Group();

    // ---------- floor ----------
    const floorGeo = new THREE.CircleGeometry(8, 64);
    const floorMat = new THREE.MeshStandardMaterial({ color: 0x1a1c22, roughness: 0.4, metalness: 0.3 });
    const floor = new THREE.Mesh(floorGeo, floorMat);
    floor.rotation.x = -Math.PI / 2;
    floor.receiveShadow = true;
    group.add(floor);

    // floor accent ring
    const ringGeo = new THREE.RingGeometry(3.2, 3.35, 64);
    const ringMat = new THREE.MeshStandardMaterial({
      color: 0x4ba3c7, emissive: 0x2a6a8c, emissiveIntensity: 0.6,
      roughness: 0.4, side: THREE.DoubleSide,
    });
    const ring = new THREE.Mesh(ringGeo, ringMat);
    ring.rotation.x = -Math.PI / 2; ring.position.y = 0.005;
    group.add(ring);

    // ---------- back wall + screen ----------
    const wallGeo = new THREE.PlaneGeometry(20, 10);
    const wallMat = new THREE.MeshStandardMaterial({ color: 0x0a0c12, roughness: 0.9 });
    const wall = new THREE.Mesh(wallGeo, wallMat);
    wall.position.set(0, 4, -5); wall.receiveShadow = true;
    group.add(wall);

    const frame = new THREE.Mesh(
      new THREE.BoxGeometry(7.4, 4.4, 0.2),
      new THREE.MeshStandardMaterial({ color: 0x1a1c24, roughness: 0.3, metalness: 0.6 })
    );
    frame.position.set(0, 4, -4.85);
    group.add(frame);

    const screenGeo = new THREE.PlaneGeometry(7, 4);
    const screenCanvas = document.createElement('canvas');
    screenCanvas.width = 1024; screenCanvas.height = 576;
    const screenCtx = screenCanvas.getContext('2d');
    drawDefaultScreen(screenCtx, screenCanvas.width, screenCanvas.height);
    const screenTex = new THREE.CanvasTexture(screenCanvas);
    screenTex.colorSpace = THREE.SRGBColorSpace;
    const screen = new THREE.Mesh(screenGeo, new THREE.MeshBasicMaterial({ map: screenTex, toneMapped: false }));
    screen.position.set(0, 4, -4.74);
    group.add(screen);

    const glow = new THREE.Mesh(
      new THREE.PlaneGeometry(8, 5),
      new THREE.MeshBasicMaterial({ color: 0x4ba3c7, transparent: true, opacity: 0.08, blending: THREE.AdditiveBlending })
    );
    glow.position.set(0, 4, -4.8);
    group.add(glow);

    // pillars + strips
    function pillar(x) {
      const p = new THREE.Mesh(
        new THREE.BoxGeometry(0.4, 9, 0.4),
        new THREE.MeshStandardMaterial({ color: 0x14161c, roughness: 0.5, metalness: 0.4 })
      );
      p.position.set(x, 4.5, -4.6); p.castShadow = true; return p;
    }
    group.add(pillar(-4.2)); group.add(pillar(4.2));
    function strip(x) {
      const s = new THREE.Mesh(
        new THREE.BoxGeometry(0.08, 6, 0.08),
        new THREE.MeshStandardMaterial({ color: 0x4ba3c7, emissive: 0x4ba3c7, emissiveIntensity: 1.5 })
      );
      s.position.set(x, 4, -4.4); return s;
    }
    const stripL = strip(-4.0); const stripR = strip(4.0);
    group.add(stripL); group.add(stripR);

    function studioLamp(x, y, color) {
      const l = new THREE.Mesh(
        new THREE.SphereGeometry(0.25, 16, 16),
        new THREE.MeshStandardMaterial({ color, emissive: color, emissiveIntensity: 2 })
      );
      l.position.set(x, y, -3); return l;
    }
    const lampL = studioLamp(-5.5, 6, 0xffb070);
    const lampR = studioLamp(5.5, 6, 0x70a0ff);
    group.add(lampL); group.add(lampR);

    // lights
    const ambient = new THREE.AmbientLight(0xffffff, 0.35); scene.add(ambient);
    const key = new THREE.DirectionalLight(0xfff0d0, 1.4);
    key.position.set(3, 6, 5); key.castShadow = true;
    key.shadow.mapSize.set(1024, 1024);
    key.shadow.camera.near = 1; key.shadow.camera.far = 20;
    key.shadow.camera.left = -6; key.shadow.camera.right = 6;
    key.shadow.camera.top = 6; key.shadow.camera.bottom = -2;
    key.shadow.bias = -0.0005; scene.add(key);
    const rim = new THREE.DirectionalLight(0x80c0ff, 0.7); rim.position.set(-4, 5, -3); scene.add(rim);
    const fill = new THREE.PointLight(0xffb070, 0.6, 15); fill.position.set(-3, 3, 4); scene.add(fill);
    const screenLight = new THREE.PointLight(0x4ba3c7, 0.8, 12); screenLight.position.set(0, 4, -3); scene.add(screenLight);

    scene.add(group);

    // ----- screen API -----
    function drawDefaultScreen(ctx, w, h) {
      const g = ctx.createLinearGradient(0, 0, w, h);
      g.addColorStop(0, '#0a1f2c'); g.addColorStop(1, '#1a3a4c');
      ctx.fillStyle = g; ctx.fillRect(0, 0, w, h);
      ctx.strokeStyle = 'rgba(75, 163, 199, 0.2)'; ctx.lineWidth = 1;
      for (let x = 0; x < w; x += 60) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke(); }
      for (let y = 0; y < h; y += 60) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke(); }
      ctx.fillStyle = 'rgba(255,255,255,0.5)';
      ctx.font = '600 36px Inter, sans-serif'; ctx.textAlign = 'center';
      ctx.fillText('METASPEAK', w / 2, h / 2 - 10);
      ctx.fillStyle = 'rgba(75, 163, 199, 0.9)';
      ctx.font = '500 18px JetBrains Mono, monospace';
      ctx.fillText('AWAITING TOPIC...', w / 2, h / 2 + 24);
    }

    let currentImage = null, nextImage = null, crossfade = 0;
    async function setScreenImage(url, caption) {
      return new Promise((resolve) => {
        const img = new Image();
        img.crossOrigin = 'anonymous';
        img.onload = () => { nextImage = { img, caption }; crossfade = 0; resolve(); };
        img.onerror = () => resolve();
        img.src = url;
      });
    }
    function clearScreen() {
      currentImage = null; nextImage = null;
      drawDefaultScreen(screenCtx, screenCanvas.width, screenCanvas.height);
      screenTex.needsUpdate = true;
    }
    function drawImageCovered(ctx, img, w, h) {
      const ar = img.width / img.height, tar = w / h;
      let dw, dh, dx, dy;
      if (ar > tar) { dh = h; dw = h * ar; dx = (w - dw) / 2; dy = 0; }
      else { dw = w; dh = w / ar; dx = 0; dy = (h - dh) / 2; }
      ctx.drawImage(img, dx, dy, dw, dh);
    }
    function updateScreen(dt) {
      const w = screenCanvas.width, h = screenCanvas.height;
      if (nextImage) {
        crossfade = Math.min(1, crossfade + dt * 1.5);
        screenCtx.fillStyle = '#000'; screenCtx.fillRect(0, 0, w, h);
        if (currentImage) {
          screenCtx.globalAlpha = 1 - crossfade;
          drawImageCovered(screenCtx, currentImage.img, w, h);
        }
        screenCtx.globalAlpha = crossfade;
        drawImageCovered(screenCtx, nextImage.img, w, h);
        screenCtx.globalAlpha = 1;
        if (nextImage.caption) {
          const grad = screenCtx.createLinearGradient(0, h - 120, 0, h);
          grad.addColorStop(0, 'rgba(0,0,0,0)');
          grad.addColorStop(1, 'rgba(0,0,0,0.7)');
          screenCtx.fillStyle = grad; screenCtx.fillRect(0, h - 120, w, 120);
          screenCtx.fillStyle = 'rgba(255,255,255,0.95)';
          screenCtx.font = '600 28px Inter, sans-serif';
          screenCtx.textAlign = 'left';
          screenCtx.fillText(nextImage.caption, 32, h - 32);
        }
        screenTex.needsUpdate = true;
        if (crossfade >= 1) { currentImage = nextImage; nextImage = null; }
      }
    }

    // ----- Environment API -----
    function applyEnv({ preset, bg, floor: floorColor, accent, ambient: ambI, keyI, fogDensity }) {
      const p = preset && ENV_PRESETS[preset] ? ENV_PRESETS[preset] : null;
      if (p) {
        scene.background = new THREE.Color(p.bg);
        scene.fog = new THREE.Fog(p.fog, 9, 24);
        floor.material.color.setHex(p.floor);
        ring.material.color.setHex(p.accent);
        ring.material.emissive.setHex(p.accent);
        stripL.material.color.setHex(p.accent); stripL.material.emissive.setHex(p.accent);
        stripR.material.color.setHex(p.accent); stripR.material.emissive.setHex(p.accent);
        ambient.intensity = p.ambient;
        key.intensity = p.key;
        fill.color.setHex(p.fillColor);
        screenLight.color.setHex(p.accent);
        glow.material.color.setHex(p.accent);
      }
      if (bg) scene.background = new THREE.Color(bg);
      if (floorColor) floor.material.color.set(floorColor);
      if (accent) {
        ring.material.color.set(accent); ring.material.emissive.set(accent);
        stripL.material.color.set(accent); stripL.material.emissive.set(accent);
        stripR.material.color.set(accent); stripR.material.emissive.set(accent);
        screenLight.color.set(accent); glow.material.color.set(accent);
      }
      if (ambI != null) ambient.intensity = ambI;
      if (keyI != null) key.intensity = keyI;
      if (fogDensity != null && scene.fog) {
        // map 0..1 → near=24..6, far=40..18
        scene.fog.near = 24 - 18 * fogDensity;
        scene.fog.far = 40 - 22 * fogDensity;
      }
    }

    // Track visible meshes separately from lights so we can hide the
    // default backdrop (floor, walls, screen, frame) when the user
    // uploads a custom .glb environment, while keeping lights intact
    // so their environment is properly lit.
    function setVisibleMeshes(visible) {
      group.visible = visible;
    }

    return {
      group, setScreenImage, clearScreen, updateScreen, screenLight,
      applyEnv, presets: Object.keys(ENV_PRESETS),
      // Surfaced for environment integration:
      screenTexture: screenTex,    // CanvasTexture — share to user's screen mesh
      setVisibleMeshes,             // hide default stage geometry while keeping lights
    };
  }

  return { build, ENV_PRESETS };
})();

window.StageFactory = StageFactory;
