/// file: public/assets/js/modules/home-diamond/scene.js

import { buildCrystalGemGeometry } from './build-crystal-geometry.js';
import {
  CAMERA_FOV,
  CAMERA_Y_OFFSET,
  CAMERA_Z,
  CLEAR_ALPHA,
  CLEAR_COLOR_HEX,
  GEM_BASE_SCALE,
  GEM_HEIGHT_FACTOR,
  GEM_OVERALL_BOOST,
  GEM_SIZE_MULT,
  GEM_WIDTH_FACTOR,
  IDLE_BOB_AMP,
  IDLE_BREATHE_AMP,
  IDLE_ROT_Y_SPEED,
  IDLE_ROT_Y_WOBBLE,
  INTRO_DURATION_SEC,
  MAT_ATTENUATION,
  MAT_CLEARCOAT,
  MAT_CLEARCOAT_ROUGH,
  MAT_COLOR,
  MAT_DISPERSION,
  MAT_ENV_INTENSITY,
  MAT_IOR,
  MAT_METALNESS,
  MAT_ROUGHNESS,
  MAT_SPECULAR_INTENSITY,
  MAT_THICKNESS,
  MAT_TRANSMISSION,
  MESH_Y_OFFSET,
  PARALLAX_CAM_X_MAX,
  PARALLAX_CAM_Y_MAX,
  PARALLAX_ROT_X_MAX,
  PARALLAX_ROT_Y_MAX,
  PARALLAX_ROT_Z_MAX,
  POST_DISABLE_MAX_WIDTH,
  RENDERER_EXPOSURE,
  SHARD_COUNT,
  SHARD_LIFE_SEC,
} from './constants.js';
import { createDarkEnvironment } from './dark-environment.js';
import { createPointerSmoother } from './pointer-smooth.js';
import { createChromaticVignettePass } from './post-pass.js';

/**
 * @param {HTMLCanvasElement} canvas
 * @param {typeof import('three')} THREE
 * @returns {() => void}
 */
export function createLuxuryDiamondScene(canvas, THREE) {
  const wrap = canvas.closest('[data-home-diamond-wrap]');

  let renderer;
  try {
    renderer = new THREE.WebGLRenderer({
      canvas,
      antialias: true,
      alpha: true,
      powerPreference: 'high-performance',
    });
  } catch (e) {
    console.warn('[home-diamond] WebGLRenderer failed', e);
    wrap?.classList.add('home-diamond-wrap--static-fallback');
    return () => {};
  }

  renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
  renderer.setClearColor(CLEAR_COLOR_HEX, CLEAR_ALPHA);
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  renderer.toneMappingExposure = RENDERER_EXPOSURE;
  renderer.outputColorSpace = THREE.SRGBColorSpace;

  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(CAMERA_FOV, 1, 0.1, 100);
  camera.position.set(0, CAMERA_Y_OFFSET, CAMERA_Z);

  const { texture: envTex, disposeBake } = createDarkEnvironment(THREE, renderer);
  scene.environment = envTex;

  const ambient = new THREE.AmbientLight(0x2a3544, 0.088);
  scene.add(ambient);

  /* Upper-right-front key — silhouette + selective highlights */
  const keyLight = new THREE.DirectionalLight(0xf8f9ff, 2.65);
  keyLight.position.set(2.85, 3.35, 4.75);
  scene.add(keyLight);

  const fillLight = new THREE.DirectionalLight(0x707a8c, 0.28);
  fillLight.position.set(-4.5, 0.15, 3.6);
  scene.add(fillLight);

  const rimLight = new THREE.DirectionalLight(0xd8e8ff, 0.92);
  rimLight.position.set(-1.2, 0.45, -5.5);
  scene.add(rimLight);

  /* Alt taper okunurluğu — çok düşük, dramatik kontrast korunur */
  const underLight = new THREE.DirectionalLight(0x556070, 0.052);
  underLight.position.set(0.3, -5.8, 2.4);
  scene.add(underLight);

  const glint = new THREE.PointLight(0xf0f4ff, 0.38, 14, 2.0);
  glint.position.set(1.35, 2.05, 3.25);
  scene.add(glint);

  const mat = new THREE.MeshPhysicalMaterial({
    color: new THREE.Color(MAT_COLOR),
    metalness: MAT_METALNESS,
    roughness: MAT_ROUGHNESS,
    transmission: MAT_TRANSMISSION,
    thickness: MAT_THICKNESS,
    ior: MAT_IOR,
    transparent: true,
    opacity: 1,
    envMapIntensity: MAT_ENV_INTENSITY,
    clearcoat: MAT_CLEARCOAT,
    clearcoatRoughness: MAT_CLEARCOAT_ROUGH,
    specularIntensity: MAT_SPECULAR_INTENSITY,
    specularColor: new THREE.Color(0xffffff),
    attenuationColor: new THREE.Color(MAT_ATTENUATION),
    attenuationDistance: 0.48,
    dispersion: MAT_DISPERSION,
    flatShading: true,
    side: THREE.DoubleSide,
  });

  const gemGeo = buildCrystalGemGeometry(THREE);
  const mesh = new THREE.Mesh(gemGeo, mat);
  mesh.position.y = MESH_Y_OFFSET;
  mesh.rotation.set(0.045, 0.16, 0);
  scene.add(mesh);

  const baseScale = GEM_BASE_SCALE;
  const sx = baseScale * GEM_WIDTH_FACTOR * GEM_OVERALL_BOOST * GEM_SIZE_MULT;
  const sy =
    baseScale * GEM_OVERALL_BOOST * GEM_SIZE_MULT * GEM_HEIGHT_FACTOR;
  const sz = baseScale * GEM_WIDTH_FACTOR * GEM_OVERALL_BOOST * GEM_SIZE_MULT;

  const pointerSmooth = createPointerSmoother();
  const postPass = createChromaticVignettePass(THREE, renderer);

  const mqPost = window.matchMedia(`(max-width: ${POST_DISABLE_MAX_WIDTH}px)`);
  const updatePostEnabled = () => {
    postPass.setEnabled(!mqPost.matches);
  };
  updatePostEnabled();

  let w = 1;
  let h = 1;
  let raf = 0;
  const clock = new THREE.Clock();
  let idlePhase = 0;

  /** @type {{ mesh: import('three').Mesh; geo: import('three').BufferGeometry; mat: import('three').MeshPhysicalMaterial; velocity: import('three').Vector3; av: import('three').Vector3; t: number }[]} */
  const shardParticles = [];
  let shattered = false;

  const resize = () => {
    const rect = canvas.getBoundingClientRect();
    w = Math.max(1, Math.floor(rect.width));
    h = Math.max(1, Math.floor(rect.height));
    renderer.setSize(w, h, false);
    camera.aspect = w / h;
    camera.updateProjectionMatrix();
    postPass.syncSize();
  };

  resize();
  requestAnimationFrame(() => {
    resize();
    requestAnimationFrame(() => resize());
  });

  const spawnShards = () => {
    const spread = 0.36;
    for (let i = 0; i < SHARD_COUNT; i++) {
      const size = 0.09 + Math.random() * 0.12;
      const geo = new THREE.TetrahedronGeometry(size, 0);
      const sm = /** @type {import('three').MeshPhysicalMaterial} */ (mat.clone());
      sm.roughness = 0.16 + Math.random() * 0.14;
      sm.transmission = 0.12 + Math.random() * 0.18;
      sm.metalness = 0.06 + Math.random() * 0.08;
      sm.envMapIntensity = 0.85 + Math.random() * 0.45;
      sm.opacity = 1;
      sm.transparent = true;
      const sh = new THREE.Mesh(geo, sm);
      sh.position.set(
        (Math.random() - 0.5) * spread * 2,
        (Math.random() - 0.5) * spread * 2,
        (Math.random() - 0.5) * spread * 2
      );
      sh.rotation.set(Math.random() * Math.PI * 2, Math.random() * Math.PI * 2, Math.random() * Math.PI * 2);
      let dir = sh.position.clone();
      if (dir.lengthSq() < 1e-4) {
        dir.set(Math.random() - 0.5, Math.random() * 0.4, Math.random() - 0.5);
      }
      dir.normalize();
      const impulse = 2.1 + Math.random() * 2.4;
      const velocity = dir.multiplyScalar(impulse);
      const av = new THREE.Vector3(
        (Math.random() - 0.5) * 14,
        (Math.random() - 0.5) * 14,
        (Math.random() - 0.5) * 14
      );
      scene.add(sh);
      shardParticles.push({ mesh: sh, geo, mat: sm, velocity, av, t: 0 });
    }
  };

  const onHeroIntroExit = () => {
    if (shattered) return;
    shattered = true;
    mesh.visible = false;
    spawnShards();
  };
  window.addEventListener('saneksa:hero-intro-exit', onHeroIntroExit);

  const onMqPost = () => updatePostEnabled();
  mqPost.addEventListener('change', onMqPost);

  const onWinResize = () => resize();
  window.addEventListener('resize', onWinResize);

  const tick = () => {
    const dt = Math.min(clock.getDelta(), 0.05);
    const t = clock.getElapsedTime();

    glint.position.x = 1.1 + Math.sin(t * 0.42) * 0.35;
    glint.position.y = 1.95 + Math.cos(t * 0.31) * 0.28;
    glint.intensity = 0.18 + Math.sin(t * 0.88) * 0.05;

    if (shattered) {
      const gravity = 3.2;
      for (let i = shardParticles.length - 1; i >= 0; i--) {
        const s = shardParticles[i];
        s.t += dt;
        s.velocity.y -= gravity * dt;
        s.mesh.position.addScaledVector(s.velocity, dt);
        s.mesh.rotation.x += s.av.x * dt;
        s.mesh.rotation.y += s.av.y * dt;
        s.mesh.rotation.z += s.av.z * dt;
        const fade = Math.max(0, 1 - s.t / SHARD_LIFE_SEC);
        s.mat.opacity = fade;
        if (s.t > SHARD_LIFE_SEC + 0.2) {
          scene.remove(s.mesh);
          s.geo.dispose();
          s.mat.dispose();
          shardParticles.splice(i, 1);
        }
      }
      postPass.render(scene, camera);
    } else {
      const ht = window.__saneksaHeroTarget;
      let tNx = 0.5;
      let tNy = 0.5;
      let active = false;
      if (ht && typeof ht.nx === 'number') {
        tNx = ht.nx;
        tNy = ht.ny;
        active = Boolean(ht.active);
      }
      const sm = pointerSmooth.step(dt, tNx, tNy, active);

      idlePhase += dt * IDLE_ROT_Y_SPEED;
      const breathe = 1 + Math.sin(t * 0.62) * IDLE_BREATHE_AMP;
      const introT = Math.min(1, t / INTRO_DURATION_SEC);
      const introEase = 1 - (1 - introT) ** 3;

      const swipeSpin =
        typeof window.__saneksaIntroSwipeProgress === 'number'
          ? window.__saneksaIntroSwipeProgress
          : 0;
      mesh.rotation.y =
        idlePhase +
        (sm.nx - 0.5) * 2 * PARALLAX_ROT_Y_MAX +
        Math.sin(t * 0.31) * IDLE_ROT_Y_WOBBLE +
        swipeSpin * Math.PI * 4.85;
      mesh.rotation.x =
        0.045 +
        (sm.ny - 0.5) * -2 * PARALLAX_ROT_X_MAX +
        Math.sin(t * 0.23) * 0.014 +
        swipeSpin * -0.22;
      mesh.rotation.z = (sm.nx - 0.5) * 2 * PARALLAX_ROT_Z_MAX;

      mesh.position.y = MESH_Y_OFFSET + Math.sin(t * 0.37) * IDLE_BOB_AMP;

      const camTx = (sm.nx - 0.5) * 2 * PARALLAX_CAM_X_MAX;
      const camTy = CAMERA_Y_OFFSET + (sm.ny - 0.5) * -2 * PARALLAX_CAM_Y_MAX;
      camera.position.x = THREE.MathUtils.lerp(camera.position.x, camTx, 0.065);
      camera.position.y = THREE.MathUtils.lerp(camera.position.y, camTy, 0.065);
      camera.position.z = CAMERA_Z;

      mesh.scale.set(sx * introEase * breathe, sy * introEase * breathe, sz * introEase * breathe);

      postPass.render(scene, camera);
    }

    raf = requestAnimationFrame(tick);
  };
  raf = requestAnimationFrame(tick);

  return () => {
    cancelAnimationFrame(raf);
    window.removeEventListener('resize', onWinResize);
    window.removeEventListener('saneksa:hero-intro-exit', onHeroIntroExit);
    mqPost.removeEventListener('change', onMqPost);
    for (const s of shardParticles) {
      scene.remove(s.mesh);
      s.geo.dispose();
      s.mat.dispose();
    }
    shardParticles.length = 0;
    scene.remove(mesh);
    scene.remove(ambient);
    scene.remove(keyLight);
    scene.remove(fillLight);
    scene.remove(rimLight);
    scene.remove(underLight);
    scene.remove(glint);
    gemGeo.dispose();
    mat.dispose();
    scene.environment = null;
    disposeBake();
    postPass.dispose();
    renderer.dispose();
  };
}
