/// file: public/assets/js/modules/home-diamond/build-crystal-geometry.js

import {
  CRYSTAL_RING_COUNT,
  CRYSTAL_SEGMENTS,
  CRYSTAL_RADIUS_JITTER,
  CRYSTAL_SEED,
  CRYSTAL_TWIST_JITTER,
  CRYSTAL_Y_JITTER,
} from './constants.js';

/**
 * @param {number} seed
 * @returns {() => number}
 */
function createSeededRandom(seed) {
  let state = seed >>> 0;
  return () => {
    state |= 0;
    state = (state + 0x6d2b79f5) | 0;
    let t = Math.imul(state ^ (state >>> 15), 1 | state);
    t ^= t + Math.imul(t ^ (t >>> 7), 61 | t);
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

/**
 * @param {import('three').Vector3Like} a
 * @param {import('three').Vector3Like} b
 */
function sub(a, b) {
  return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
}

/**
 * @param {import('three').Vector3Like} a
 * @param {import('three').Vector3Like} b
 */
function cross(a, b) {
  return {
    x: a.y * b.z - a.z * b.y,
    y: a.z * b.x - a.x * b.z,
    z: a.x * b.y - a.y * b.x,
  };
}

function len(v) {
  return Math.hypot(v.x, v.y, v.z);
}

function normalize(v) {
  const l = len(v) || 1;
  return { x: v.x / l, y: v.y / l, z: v.z / l };
}

/** Silhouette-friendly smooth blend (no hard shoulder in outline). */
function smooth01(u) {
  const t = Math.min(1, Math.max(0, u));
  return t * t * (3 - 2 * t);
}

/**
 * Canonical tear-drop meridian: t=0 apex (top), t=1 bottom tip.
 * Max radius at t≈0.60 (lower-middle). No flat top, no bottle neck.
 */
export const TEARDROP_PROFILE_KEYS = [
  { t: 0, r: 0.034 },
  { t: 0.05, r: 0.041 },
  { t: 0.1, r: 0.056 },
  { t: 0.18, r: 0.079 },
  { t: 0.25, r: 0.108 },
  { t: 0.35, r: 0.188 },
  { t: 0.45, r: 0.298 },
  { t: 0.52, r: 0.385 },
  { t: 0.6, r: 0.468 },
  { t: 0.68, r: 0.412 },
  { t: 0.78, r: 0.238 },
  { t: 0.86, r: 0.148 },
  { t: 0.92, r: 0.086 },
  { t: 0.97, r: 0.046 },
  { t: 1, r: 0.022 },
];

/**
 * @param {number} t normalized top→bottom
 */
export function teardropMeridianRadius(t) {
  const u = Math.min(1, Math.max(0, t));
  const keys = TEARDROP_PROFILE_KEYS;
  for (let i = 0; i < keys.length - 1; i++) {
    const a = keys[i];
    const b = keys[i + 1];
    if (u <= b.t) {
      const span = b.t - a.t || 1;
      const w = smooth01((u - a.t) / span);
      return a.r + (b.r - a.r) * w;
    }
  }
  return keys[keys.length - 1].r;
}

function profileY(t) {
  return 0.498 - t * 1.02;
}

/** Less noise near poles + extra damp in upper third (no vase shoulder). */
function jitterDampen(t) {
  const pole = Math.min(t, 1 - t);
  const base = 0.18 + 0.82 * smooth01(pole * 5.2);
  const upper = t < 0.28 ? 0.35 + 0.65 * smooth01(t / 0.28) : 1;
  return base * upper;
}

/**
 * @param {import('three').Vector3Like} p0
 * @param {import('three').Vector3Like} p1
 * @param {import('three').Vector3Like} p2
 * @param {number[]} pos
 * @param {number[]} norm
 */
function pushTriangle(p0, p1, p2, pos, norm) {
  const e1 = sub(p1, p0);
  const e2 = sub(p2, p0);
  const n = normalize(cross(e1, e2));
  for (const p of [p0, p1, p2]) {
    pos.push(p.x, p.y, p.z);
    norm.push(n.x, n.y, n.z);
  }
}

/**
 * Faceted obsidian tear-drop: stacked rings + soft apex + short bottom tip.
 * @param {typeof import('three')} THREE
 * @param {{ seed?: number; rings?: number; segments?: number }} [opts]
 * @returns {import('three').BufferGeometry}
 */
export function buildCrystalGemGeometry(THREE, opts = {}) {
  const seed = opts.seed ?? CRYSTAL_SEED;
  const R = opts.rings ?? CRYSTAL_RING_COUNT;
  const N = opts.segments ?? CRYSTAL_SEGMENTS;
  const rnd = createSeededRandom(seed);

  const rings = /** @type {{ x: number; y: number; z: number }[][]} */ ([]);

  for (let i = 0; i < R; i++) {
    const t = R === 1 ? 0 : i / (R - 1);
    const ring = [];
    const damp = jitterDampen(t);
    const baseY =
      profileY(t) + (rnd() - 0.5) * CRYSTAL_Y_JITTER * 2 * damp;
    const baseR =
      teardropMeridianRadius(t) *
      (1 + (rnd() - 0.5) * CRYSTAL_RADIUS_JITTER * 2 * damp);
    const twist =
      (rnd() - 0.5) *
      CRYSTAL_TWIST_JITTER *
      damp *
      (0.92 - t * 0.28);

    for (let s = 0; s < N; s++) {
      const ang =
        (s / N) * Math.PI * 2 + twist + (rnd() - 0.5) * 0.028 * damp;
      const rV = baseR * (1 + (rnd() - 0.5) * CRYSTAL_RADIUS_JITTER * damp);
      ring.push({
        x: Math.cos(ang) * rV,
        y: baseY,
        z: Math.sin(ang) * rV,
      });
    }
    rings.push(ring);
  }

  const positions = [];
  const normals = [];

  /* Soft pointed apex — first ring already tiny; short cone */
  const apex = {
    x: (rnd() - 0.5) * 0.01,
    y: rings[0][0].y + 0.018 + (rnd() - 0.5) * 0.005,
    z: (rnd() - 0.5) * 0.01,
  };
  const r0 = rings[0];
  for (let s = 0; s < N; s++) {
    const s2 = (s + 1) % N;
    pushTriangle(apex, r0[s], r0[s2], positions, normals);
  }

  for (let i = 0; i < R - 1; i++) {
    const a = rings[i];
    const b = rings[i + 1];
    for (let s = 0; s < N; s++) {
      const s2 = (s + 1) % N;
      const A0 = a[s];
      const A1 = a[s2];
      const B0 = b[s];
      const B1 = b[s2];
      pushTriangle(A0, B0, B1, positions, normals);
      pushTriangle(A0, B1, A1, positions, normals);
    }
  }

  const last = rings[R - 1];
  let cx = 0;
  let cy = last[0].y;
  let cz = 0;
  for (const p of last) {
    cx += p.x;
    cy += p.y;
    cz += p.z;
  }
  cx /= N;
  cy /= N;
  cz /= N;

  /* Sculptural short tip — not needle tail */
  const culet = {
    x: cx + (rnd() - 0.5) * 0.003,
    y: cy - 0.032 - Math.abs(rnd() * 0.004),
    z: cz + (rnd() - 0.5) * 0.003,
  };
  for (let s = 0; s < N; s++) {
    const s2 = (s + 1) % N;
    pushTriangle(culet, last[s2], last[s], positions, normals);
  }

  const geo = new THREE.BufferGeometry();
  geo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
  geo.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
  geo.computeBoundingSphere();
  return geo;
}
