// Procgen meshes for spider bots, turrets and bombs. No textures — emissive + bloom does the work.
import * as THREE from 'three';

export interface BotRig {
  group: THREE.Group;
  head: THREE.Group;
  legs: THREE.Group[];
  bodyMat: THREE.MeshStandardMaterial;
  capMat: THREE.MeshBasicMaterial;
}

export function makeSpiderBot(capColor: number, bodyColor: number, ringColor?: number): BotRig {
  const group = new THREE.Group();
  const bodyMat = new THREE.MeshStandardMaterial({
    color: bodyColor,
    roughness: 0.5,
    metalness: 0.4,
    emissive: 0xffffff,
    emissiveIntensity: 0,
  });

  const body = new THREE.Mesh(new THREE.SphereGeometry(0.22, 14, 10), bodyMat);
  body.scale.y = 0.62;
  body.position.y = 0.2;
  group.add(body);

  const legs: THREE.Group[] = [];
  const upGeo = new THREE.BoxGeometry(0.2, 0.035, 0.035);
  upGeo.translate(0.1, 0, 0);
  const loGeo = new THREE.BoxGeometry(0.24, 0.028, 0.028);
  loGeo.translate(0.12, 0, 0);
  for (let i = 0; i < 8; i++) {
    const a = (i / 8) * Math.PI * 2 + Math.PI / 8;
    const leg = new THREE.Group();
    leg.position.set(Math.cos(a) * 0.12, 0.18, Math.sin(a) * 0.12);
    leg.rotation.y = -a;
    leg.userData.baseYaw = -a;
    leg.userData.phase = (i % 2) * Math.PI + i * 0.4;
    const upper = new THREE.Mesh(upGeo, bodyMat);
    upper.rotation.z = 0.55;
    leg.add(upper);
    const lower = new THREE.Mesh(loGeo, bodyMat);
    lower.position.set(0.17, 0.104, 0);
    lower.rotation.z = -1.18;
    leg.add(lower);
    leg.userData.upper = upper;
    leg.userData.lower = lower;
    group.add(leg);
    legs.push(leg);
  }

  const head = new THREE.Group();
  head.position.y = 0.37;
  const base = new THREE.Mesh(new THREE.CylinderGeometry(0.09, 0.13, 0.1, 12), bodyMat);
  head.add(base);
  const barrel = new THREE.Mesh(new THREE.BoxGeometry(0.05, 0.05, 0.32), bodyMat);
  barrel.position.set(0, 0.03, 0.18);
  head.add(barrel);
  const capMat = new THREE.MeshBasicMaterial({ color: capColor, toneMapped: false });
  const cap = new THREE.Mesh(new THREE.SphereGeometry(0.075, 12, 8), capMat);
  cap.position.y = 0.1;
  head.add(cap);
  group.add(head);

  // self-glow so bots read against the dark floor
  const glow = new THREE.PointLight(capColor, 1.6, 3, 1.5);
  glow.position.y = 0.7;
  group.add(glow);
  group.scale.setScalar(1.18);

  if (ringColor !== undefined) {
    const ring = new THREE.Mesh(
      new THREE.RingGeometry(0.3, 0.37, 36),
      new THREE.MeshBasicMaterial({
        color: ringColor,
        transparent: true,
        opacity: 0.8,
        toneMapped: false,
        side: THREE.DoubleSide,
        depthWrite: false,
      })
    );
    ring.rotation.x = -Math.PI / 2;
    ring.position.y = 0.02;
    group.add(ring);
  }
  return { group, head, legs, bodyMat, capMat };
}

/** Heavy enemy tier: a bulked-up spider, ~2x scale, armor plates + red core. */
export function makeGigabot(): BotRig {
  const rig = makeSpiderBot(0xff3030, 0x33272c);
  rig.group.scale.setScalar(2.15);
  // armor plates on the back so the silhouette reads "heavy"
  const plateMat = new THREE.MeshStandardMaterial({ color: 0x4a3038, roughness: 0.4, metalness: 0.6 });
  for (let i = 0; i < 3; i++) {
    const plate = new THREE.Mesh(new THREE.BoxGeometry(0.26 - i * 0.05, 0.04, 0.18), plateMat);
    plate.position.set(0, 0.3 + i * 0.045, -0.06 + i * 0.04);
    plate.rotation.x = 0.25;
    rig.group.add(plate);
  }
  // twin shoulder rocket pods
  const podMat = new THREE.MeshStandardMaterial({ color: 0x2c2228, roughness: 0.5, metalness: 0.5 });
  for (const sx of [-1, 1]) {
    const pod = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.09, 0.16), podMat);
    pod.position.set(sx * 0.17, 0.34, -0.02);
    rig.group.add(pod);
    const tip = new THREE.Mesh(
      new THREE.SphereGeometry(0.028, 8, 6),
      new THREE.MeshBasicMaterial({ color: 0xff6a30, toneMapped: false })
    );
    tip.position.set(sx * 0.17, 0.34, 0.07);
    rig.group.add(tip);
  }
  return rig;
}

/** The objective: glowing trunk + crystal canopy. Pulses; unlock brightens it. */
export interface TreeRig {
  group: THREE.Group;
  canopyMat: THREE.MeshStandardMaterial;
  beam: THREE.Mesh;
  beamMat: THREE.MeshBasicMaterial;
  light: THREE.PointLight;
}
export function makeTreeOfLife(): TreeRig {
  const group = new THREE.Group();
  const trunkMat = new THREE.MeshStandardMaterial({ color: 0x3a2f26, roughness: 0.85, metalness: 0.05 });
  const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.07, 0.16, 0.7, 7), trunkMat);
  trunk.position.y = 0.35;
  group.add(trunk);
  // gnarled root flares
  for (let i = 0; i < 4; i++) {
    const a = (i / 4) * Math.PI * 2 + 0.5;
    const root = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.06, 0.3, 5), trunkMat);
    root.position.set(Math.cos(a) * 0.14, 0.1, Math.sin(a) * 0.14);
    root.rotation.z = Math.cos(a) * 0.7;
    root.rotation.x = -Math.sin(a) * 0.7;
    group.add(root);
  }
  const canopyMat = new THREE.MeshStandardMaterial({
    color: 0x1d5c38,
    roughness: 0.4,
    metalness: 0.1,
    emissive: 0x35ff8a,
    emissiveIntensity: 0.55,
  });
  // crystal foliage clusters
  const blobs: [number, number, number, number][] = [
    [0, 0.92, 0, 0.3], [0.22, 0.8, 0.08, 0.18], [-0.2, 0.84, -0.06, 0.2],
    [0.05, 0.78, -0.22, 0.16], [-0.06, 0.82, 0.22, 0.17], [0.0, 1.12, 0.05, 0.16],
  ];
  for (const [x, y, z, r] of blobs) {
    const b = new THREE.Mesh(new THREE.IcosahedronGeometry(r, 0), canopyMat);
    b.position.set(x, y, z);
    b.rotation.set(x * 9, y * 7, z * 9);
    group.add(b);
  }
  const light = new THREE.PointLight(0x46ffa0, 2.2, 6, 1.6);
  light.position.y = 1.1;
  group.add(light);
  // unlock beacon: vertical light beam, hidden until defenses fall
  const beamMat = new THREE.MeshBasicMaterial({
    color: 0x7dffc0, transparent: true, opacity: 0,
    blending: THREE.AdditiveBlending, depthWrite: false, toneMapped: false,
  });
  const beam = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.32, 7, 12, 1, true), beamMat);
  beam.position.y = 3.8;
  beam.visible = false;
  group.add(beam);
  return { group, canopyMat, beam, beamMat, light };
}

/** Glowing scrap chunk dropped by dead bots. */
export function makeScrapMesh(): THREE.Mesh {
  const mesh = new THREE.Mesh(
    new THREE.OctahedronGeometry(0.08, 0),
    new THREE.MeshStandardMaterial({
      color: 0x6a4a28,
      roughness: 0.35,
      metalness: 0.8,
      emissive: 0xffa030,
      emissiveIntensity: 1.4,
    })
  );
  mesh.scale.y = 0.7;
  return mesh;
}

/** Deployable sentry orb: floating emissive core + halo ring. */
export function makeOrbMesh(): THREE.Group {
  const group = new THREE.Group();
  const core = new THREE.Mesh(
    new THREE.IcosahedronGeometry(0.12, 1),
    new THREE.MeshBasicMaterial({ color: 0x66f0ff, toneMapped: false })
  );
  group.add(core);
  const shell = new THREE.Mesh(
    new THREE.IcosahedronGeometry(0.17, 0),
    new THREE.MeshStandardMaterial({
      color: 0x2a3a4a, roughness: 0.3, metalness: 0.7,
      transparent: true, opacity: 0.55,
    })
  );
  group.add(shell);
  const ring = new THREE.Mesh(
    new THREE.TorusGeometry(0.24, 0.015, 6, 28),
    new THREE.MeshBasicMaterial({ color: 0x66f0ff, toneMapped: false, transparent: true, opacity: 0.8 })
  );
  ring.rotation.x = Math.PI / 2;
  group.add(ring);
  const light = new THREE.PointLight(0x66f0ff, 2, 4, 1.6);
  group.add(light);
  return group;
}

export function makeTurretRig(capColor: number): BotRig {
  const group = new THREE.Group();
  const bodyMat = new THREE.MeshStandardMaterial({
    color: 0x3a3f4d,
    roughness: 0.45,
    metalness: 0.5,
    emissive: 0xffffff,
    emissiveIntensity: 0,
  });
  const pedestal = new THREE.Mesh(new THREE.CylinderGeometry(0.16, 0.27, 0.36, 8), bodyMat);
  pedestal.position.y = 0.18;
  group.add(pedestal);

  const head = new THREE.Group();
  head.position.y = 0.42;
  const dome = new THREE.Mesh(
    new THREE.SphereGeometry(0.16, 12, 8, 0, Math.PI * 2, 0, Math.PI / 2),
    bodyMat
  );
  head.add(dome);
  const barrel = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, 0.4), bodyMat);
  barrel.position.set(0, 0.06, 0.22);
  head.add(barrel);
  const capMat = new THREE.MeshBasicMaterial({ color: capColor, toneMapped: false });
  const cap = new THREE.Mesh(new THREE.SphereGeometry(0.06, 10, 8), capMat);
  cap.position.y = 0.17;
  head.add(cap);
  group.add(head);
  const glow = new THREE.PointLight(capColor, 1.4, 3, 1.5);
  glow.position.y = 0.8;
  group.add(glow);
  return { group, head, legs: [], bodyMat, capMat };
}

export function makeBombMesh(): { group: THREE.Group; capMat: THREE.MeshBasicMaterial } {
  const group = new THREE.Group();
  const mat = new THREE.MeshStandardMaterial({ color: 0x222630, roughness: 0.35, metalness: 0.6 });
  const ball = new THREE.Mesh(new THREE.SphereGeometry(0.16, 12, 10), mat);
  ball.position.y = 0.16;
  group.add(ball);
  const capMat = new THREE.MeshBasicMaterial({ color: 0xff5522, toneMapped: false });
  const tip = new THREE.Mesh(new THREE.SphereGeometry(0.055, 8, 8), capMat);
  tip.position.y = 0.34;
  group.add(tip);
  return { group, capMat };
}

/** Small extruded-capsule rocket, nose along +Z, glowing red rear + flame light. */
export function makeRocketMesh(): THREE.Group {
  const group = new THREE.Group();
  // capsule outline in XY (long axis = Y), extruded then rotated so it flies along +Z
  const r = 0.055;
  const half = 0.11;
  const shape = new THREE.Shape();
  shape.absarc(0, half, r, 0, Math.PI, false);
  shape.lineTo(-r, -half);
  shape.absarc(0, -half, r, Math.PI, Math.PI * 2, false);
  shape.closePath();
  const geo = new THREE.ExtrudeGeometry(shape, {
    depth: 0.06,
    bevelEnabled: true,
    bevelThickness: 0.025,
    bevelSize: 0.02,
    bevelSegments: 2,
  });
  geo.center();
  geo.rotateX(-Math.PI / 2); // Y -> Z: nose forward
  const body = new THREE.Mesh(
    geo,
    new THREE.MeshStandardMaterial({ color: 0x6a7388, roughness: 0.35, metalness: 0.7 })
  );
  group.add(body);
  // red-hot rear
  const rear = new THREE.Mesh(
    new THREE.SphereGeometry(0.055, 10, 8),
    new THREE.MeshBasicMaterial({ color: 0xff3318, toneMapped: false })
  );
  rear.position.z = -0.16;
  group.add(rear);
  const light = new THREE.PointLight(0xff5522, 2.4, 3.2, 1.6);
  light.position.z = -0.2;
  group.add(light);
  return group;
}

/** Dispose a rig's geometries (materials are per-rig too). */
export function disposeGroup(g: THREE.Object3D): void {
  g.traverse((o) => {
    const m = o as THREE.Mesh;
    if (m.isMesh) {
      m.geometry.dispose();
      const mat = m.material;
      if (Array.isArray(mat)) mat.forEach((x) => x.dispose());
      else mat.dispose();
    }
  });
}
