import { Mesh, VertexData, Vector3 } from '@babylonjs/core';

/**
 * Reusable anatomical cross-section mesh generator.
 * ring (stacked along Y): { y, rx, rz, cx, cz, rot, flatFront, fullBack, sideBias, trim }
 */
export function ringPoint(ring, i, N = 30) {
  const t = (i / N) * Math.PI * 2;
  let x = ring.rx * Math.cos(t);
  let z = ring.rz * Math.sin(t);
  if (z > 0) z *= 1 - (ring.flatFront || 0);
  if (z < 0) z *= 1 + (ring.fullBack || 0);
  const rot = ring.rot || 0;
  const cr = Math.cos(rot);
  const sr = Math.sin(rot);
  const xr = x * cr - z * sr + (ring.sideBias || 0);
  const zr = x * sr + z * cr;
  let y = ring.y;
  if (ring.trim) {
    const lat = Math.max(Math.cos(t), 0);
    const med = Math.max(-Math.cos(t), 0);
    const ant = Math.max(Math.sin(t), 0);
    const pos = Math.max(-Math.sin(t), 0);
    y += (ring.trim.lateral || 0) * lat + (ring.trim.medial || 0) * med + (ring.trim.anterior || 0) * ant + (ring.trim.posterior || 0) * pos;
  }
  return new Vector3((ring.cx || 0) + xr, y, (ring.cz || 0) + zr);
}

export function ringSurfacePoint(ring, theta) {
  const N = 30;
  const i = ((Math.round((theta / (Math.PI * 2)) * N) % N) + N) % N;
  return ringPoint(ring, i, N);
}

export function createAnatomicalSectionMesh(scene, name, rings, { closeTop = false, closeBottom = false } = {}) {
  const R = rings.length;
  const N = 30;
  const positions = [];
  const indices = [];

  const all = rings.map((r) => {
    const pts = [];
    for (let i = 0; i < N; i++) pts.push(ringPoint(r, i, N));
    return pts;
  });
  all.forEach((pts) => pts.forEach((p) => positions.push(p.x, p.y, p.z)));

  for (let j = 0; j < R - 1; j++) {
    for (let i = 0; i < N; i++) {
      const i2 = (i + 1) % N;
      const a = j * N + i;
      const b = j * N + i2;
      const c = (j + 1) * N + i2;
      const d = (j + 1) * N + i;
      indices.push(a, d, c, a, c, b);
    }
  }

  if (closeBottom) {
    const p0 = all[0];
    let ax = 0, az = 0;
    for (const p of p0) { ax += p.x; az += p.z; }
    ax /= N; az /= N;
    const cIdx = positions.length / 3;
    positions.push(ax, rings[0].y, az);
    for (let i = 0; i < N; i++) {
      const i2 = (i + 1) % N;
      indices.push(cIdx, i2, i);
    }
  }
  if (closeTop) {
    const pT = all[R - 1];
    let ax = 0, az = 0;
    for (const p of pT) { ax += p.x; az += p.z; }
    ax /= N; az /= N;
    const cIdx = positions.length / 3;
    positions.push(ax, rings[R - 1].y, az);
    const base = (R - 1) * N;
    for (let i = 0; i < N; i++) {
      const i2 = (i + 1) % N;
      indices.push(cIdx, base + i, base + i2);
    }
  }

  const normals = [];
  VertexData.ComputeNormals(positions, indices, normals);
  const vd = new VertexData();
  vd.positions = positions;
  vd.indices = indices;
  vd.normals = normals;
  const mesh = new Mesh(name, scene);
  vd.applyToMesh(mesh);
  return mesh;
}

export function createFootSectionMesh(scene, name, rings, { closeTop = false, closeBottom = false } = {}) {
  const R = rings.length;
  const N = 28;
  const positions = [];
  const indices = [];

  const all = rings.map((r) => {
    const pts = [];
    for (let i = 0; i < N; i++) {
      const t = (i / N) * Math.PI * 2;
      let x = r.rx * Math.cos(t);
      let y = r.ry * Math.sin(t);
      if (y < 0) y *= 1 - (r.flatSole || 0);
      const rot = r.rot || 0;
      const cr = Math.cos(rot);
      const sr = Math.sin(rot);
      const xr = x * cr - y * sr;
      const yr = x * sr + y * cr;
      pts.push(new Vector3((r.cx || 0) + xr, (r.cy || 0) + yr, r.z));
    }
    return pts;
  });
  all.forEach((pts) => pts.forEach((p) => positions.push(p.x, p.y, p.z)));

  for (let j = 0; j < R - 1; j++) {
    for (let i = 0; i < N; i++) {
      const i2 = (i + 1) % N;
      const a = j * N + i;
      const b = j * N + i2;
      const c = (j + 1) * N + i2;
      const d = (j + 1) * N + i;
      indices.push(a, c, d, a, b, c);
    }
  }

  const cap = (ri, isBottom) => {
    const pts = all[ri];
    let ax = 0, ay = 0;
    for (const p of pts) { ax += p.x; ay += p.y; }
    ax /= N; ay /= N;
    const cIdx = positions.length / 3;
    positions.push(ax, ay, rings[ri].z);
    const base = ri * N;
    for (let i = 0; i < N; i++) {
      const i2 = (i + 1) % N;
      if (isBottom) indices.push(cIdx, i2, i);
      else indices.push(cIdx, base + i, base + i2);
    }
  };
  if (closeBottom) cap(0, true);
  if (closeTop) cap(R - 1, false);

  const normals = [];
  VertexData.ComputeNormals(positions, indices, normals);
  const vd = new VertexData();
  vd.positions = positions;
  vd.indices = indices;
  vd.normals = normals;
  const mesh = new Mesh(name, scene);
  vd.applyToMesh(mesh);
  return mesh;
}

/**
 * Convert measured envelope stations (mm) into scene-unit anatomical rings.
 * station: { height_from_origin_mm, total_width_mm, total_depth_mm, max_medial_mm,
 *            max_lateral_mm, max_anterior_mm, max_posterior_mm, station_name, shape_classification }
 * inset (0..1) shrinks the section (e.g. socket inside cover).
 */
export function convertStationsToRings(stations, mmPerUnit, baseY, inset = 1) {
  if (!stations || !stations.length) return null;
  const usable = stations
    .filter((s) => s && (s.total_width_mm || (s.max_medial_mm != null && s.max_lateral_mm != null)) && (s.total_depth_mm || (s.max_anterior_mm != null && s.max_posterior_mm != null)))
    .slice()
    .sort((a, b) => (a.height_from_origin_mm || 0) - (b.height_from_origin_mm || 0));
  if (!usable.length) return null;
  const minH = usable[0].height_from_origin_mm || 0;
  return usable.map((s) => {
    const width = s.total_width_mm != null ? s.total_width_mm : (s.max_medial_mm || 0) + (s.max_lateral_mm || 0);
    const depth = s.total_depth_mm != null ? s.total_depth_mm : (s.max_anterior_mm || 0) + (s.max_posterior_mm || 0);
    const cx = s.max_lateral_mm != null && s.max_medial_mm != null ? (s.max_lateral_mm - s.max_medial_mm) / 2 / mmPerUnit : 0;
    const cz = s.max_anterior_mm != null && s.max_posterior_mm != null ? (s.max_anterior_mm - s.max_posterior_mm) / 2 / mmPerUnit : 0;
    return {
      y: baseY + ((s.height_from_origin_mm || 0) - minH) / mmPerUnit,
      rx: Math.max((width / 2 / mmPerUnit) * inset, 0.05),
      rz: Math.max((depth / 2 / mmPerUnit) * inset, 0.05),
      cx: cx * inset,
      cz: cz * inset,
      flatFront: 0,
      fullBack: 0,
      sideBias: 0,
      name: s.station_name,
      source: s.shape_classification || 'Workshop measured',
    };
  });
}