// @ts-nocheck
import * as THREE from 'three';

/* ── Renderer factory ── */
export const createRenderer = function (container: HTMLElement, opts: any = {}) {
  const renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: opts.alpha || false,
    powerPreference: 'high-performance',
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(container.clientWidth, container.clientHeight);
  renderer.setClearColor(opts.clearColor || 0x040810, 1);
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  renderer.toneMappingExposure = 1.0;
  container.appendChild(renderer.domElement);
  renderer.domElement.style.display = 'block';
  return renderer;
};

/* ── Resize helper ── */
export const handleResize = function (renderer: any, camera: any, container: HTMLElement) {
  const w = container.clientWidth;
  const h = container.clientHeight;
  renderer.setSize(w, h);
  if (camera.isPerspectiveCamera) {
    camera.aspect = w / h;
    camera.updateProjectionMatrix();
  }
};

/* ── Star field (Points) ── */
export const createStarField = function (count = 2000, spread = 2000) {
  const positions = new Float32Array(count * 3);
  const colors = new Float32Array(count * 3);
  const sizes = new Float32Array(count);
  for (let i = 0; i < count; i++) {
    positions[i * 3]     = (Math.random() - 0.5) * spread;
    positions[i * 3 + 1] = (Math.random() - 0.5) * spread;
    positions[i * 3 + 2] = (Math.random() - 0.5) * spread;
    const brightness = 0.4 + Math.random() * 0.6;
    colors[i * 3]     = brightness;
    colors[i * 3 + 1] = brightness;
    colors[i * 3 + 2] = brightness + Math.random() * 0.15;
    sizes[i] = 0.5 + Math.random() * 1.5;
  }
  const geo = new THREE.BufferGeometry();
  geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
  geo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
  geo.setAttribute('size', new THREE.BufferAttribute(sizes, 1));

  const mat = new THREE.PointsMaterial({
    size: 1.5,
    vertexColors: true,
    transparent: true,
    opacity: 0.8,
    sizeAttenuation: true,
  });
  return new THREE.Points(geo, mat);
};

/* ── Nebula background (soft sphere) ── */
export const addNebula = function (scene: any, color = 0x22d3ee, pos: [number, number, number] = [0, 0, -500], scale = 600) {
  const geo = new THREE.SphereGeometry(1, 16, 16);
  const mat = new THREE.MeshBasicMaterial({
    color,
    transparent: true,
    opacity: 0.015,
    side: THREE.BackSide,
  });
  const mesh = new THREE.Mesh(geo, mat);
  mesh.position.set(...pos);
  mesh.scale.setScalar(scale);
  scene.add(mesh);
  return mesh;
};

/* ── Ship mesh (wedge shape) ── */
export const createShipMesh = function (color = 0xc8d6e5, emissive = 0xf0a030, size = 1) {
  const s = size;
  // Wedge-shaped ship using BufferGeometry
  const vertices = new Float32Array([
    // Top face
    14*s, 0, 0,   -8*s, 0, -6*s,   -8*s, 0, 6*s,
    // Bottom face
    14*s, -2*s, 0,  -8*s, -2*s, 6*s,  -8*s, -2*s, -6*s,
    // Front
    14*s, 0, 0,    14*s, -2*s, 0,   -8*s, -2*s, 6*s,
    14*s, 0, 0,    -8*s, -2*s, 6*s,  -8*s, 0, 6*s,
    // Front-right
    14*s, 0, 0,    -8*s, 0, -6*s,   14*s, -2*s, 0,
    14*s, -2*s, 0, -8*s, 0, -6*s,   -8*s, -2*s, -6*s,
    // Back
    -8*s, 0, -6*s,  -8*s, 0, 6*s,   -8*s, -2*s, 6*s,
    -8*s, 0, -6*s,  -8*s, -2*s, 6*s,  -8*s, -2*s, -6*s,
    // Left side
    -8*s, 0, 6*s,   -8*s, -2*s, 6*s,  14*s, -2*s, 0,
    -8*s, 0, 6*s,   14*s, -2*s, 0,   14*s, 0, 0,
    // Right side
    14*s, 0, 0,     14*s, -2*s, 0,   -8*s, -2*s, -6*s,
    14*s, 0, 0,     -8*s, -2*s, -6*s, -8*s, 0, -6*s,
  ]);
  const geo = new THREE.BufferGeometry();
  geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
  geo.computeVertexNormals();

  const mat = new THREE.MeshStandardMaterial({
    color,
    emissive,
    emissiveIntensity: 0.15,
    metalness: 0.6,
    roughness: 0.3,
    flatShading: true,
  });
  const mesh = new THREE.Mesh(geo, mat);
  mesh.castShadow = true;
  return mesh;
};

/* ── Engine glow ── */
export const createEngineGlow = function (color = 0x22d3ee, intensity = 2, distance = 15) {
  const light = new THREE.PointLight(color, intensity, distance);
  return light;
};

export const createEngineTrail = function (color = 0x22d3ee, particleCount = 30) {
  const positions = new Float32Array(particleCount * 3);
  const geo = new THREE.BufferGeometry();
  geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
  const mat = new THREE.PointsMaterial({
    color,
    size: 1.5,
    transparent: true,
    opacity: 0.5,
    blending: THREE.AdditiveBlending,
    depthWrite: false,
  });
  return new THREE.Points(geo, mat);
};

/* ── Grid plane ── */
export const createGrid = function (size = 600, divisions = 30, color = 0x0d1520) {
  const grid = new THREE.GridHelper(size, divisions, color, color);
  grid.material.transparent = true;
  grid.material.opacity = 0.25;
  // GridHelper is XZ-plane by default — keep as is
  return grid;
};

/* ── Glow sprite (for star systems, explosions) ── */
export const createGlowSprite = function (color = 0xffffff, size = 5) {
  const canvas = document.createElement('canvas');
  canvas.width = 64;
  canvas.height = 64;
  const ctx = canvas.getContext('2d');
  const gradient = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
  gradient.addColorStop(0, `rgba(255,255,255,0.8)`);
  gradient.addColorStop(0.2, `rgba(255,255,255,0.4)`);
  gradient.addColorStop(0.5, `rgba(255,255,255,0.1)`);
  gradient.addColorStop(1, `rgba(255,255,255,0)`);
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, 64, 64);

  const texture = new THREE.CanvasTexture(canvas);
  const mat = new THREE.SpriteMaterial({
    map: texture,
    color,
    transparent: true,
    blending: THREE.AdditiveBlending,
    depthWrite: false,
  });
  const sprite = new THREE.Sprite(mat);
  sprite.scale.setScalar(size);
  return sprite;
};

/* ── Text label (Sprite with canvas texture) ── */
export const createLabel = function (text, color = '#d4dce8', fontSize = 24) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  ctx.font = `${fontSize}px JetBrains Mono, ui-monospace, monospace`;
  const metrics = ctx.measureText(text);
  const width = Math.ceil(metrics.width) + 16;
  const height = fontSize + 12;

  canvas.width = width;
  canvas.height = height;

  ctx.font = `${fontSize}px JetBrains Mono, ui-monospace, monospace`;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = color;
  ctx.fillText(text, width / 2, height / 2);

  const texture = new THREE.CanvasTexture(canvas);
  texture.minFilter = THREE.LinearFilter;
  const mat = new THREE.SpriteMaterial({
    map: texture,
    transparent: true,
    depthTest: false,
    depthWrite: false,
  });
  const sprite = new THREE.Sprite(mat);
  const aspect = width / height;
  sprite.scale.set(aspect * 1.2, 1.2, 1);
  return sprite;
};

/* ── Shield sphere ── */
export const createShield = function (radius = 2.5, color = 0x22d3ee, opacity = 0.08) {
  const geo = new THREE.SphereGeometry(radius, 24, 24);
  const mat = new THREE.MeshBasicMaterial({
    color,
    transparent: true,
    opacity,
    side: THREE.DoubleSide,
    depthWrite: false,
  });
  return new THREE.Mesh(geo, mat);
};

/* ── Projectile ── */
export const createProjectile = function (type = 'bullet') {
  let mesh;
  if (type === 'beam') {
    const geo = new THREE.CylinderGeometry(0.05, 0.05, 1, 4);
    geo.rotateX(Math.PI / 2);
    const mat = new THREE.MeshBasicMaterial({ color: 0xef4444, transparent: true, opacity: 0.9 });
    mesh = new THREE.Mesh(geo, mat);
  } else if (type === 'missile') {
    const geo = new THREE.SphereGeometry(0.15, 6, 6);
    const mat = new THREE.MeshBasicMaterial({ color: 0xf0a030 });
    mesh = new THREE.Mesh(geo, mat);
  } else if (type === 'pulse') {
    const geo = new THREE.RingGeometry(0.5, 1, 32);
    const mat = new THREE.MeshBasicMaterial({ color: 0xa78bfa, transparent: true, opacity: 0.7, side: THREE.DoubleSide });
    mesh = new THREE.Mesh(geo, mat);
  } else {
    const geo = new THREE.SphereGeometry(0.1, 4, 4);
    const mat = new THREE.MeshBasicMaterial({ color: 0xf0a030 });
    mesh = new THREE.Mesh(geo, mat);
  }
  return mesh;
};

/* ── Impact flash ── */
export const createImpact = function (color = 0xef4444, size = 2) {
  return createGlowSprite(color, size);
};

/* ── Asteroid mesh ── */
export const createAsteroid = function (size = 1, color = 0x3d2a5c) {
  const geo = new THREE.IcosahedronGeometry(size, 0);
  // Perturb vertices for rocky look
  const pos = geo.attributes.position;
  for (let i = 0; i < pos.count; i++) {
    const x = pos.getX(i);
    const y = pos.getY(i);
    const z = pos.getZ(i);
    const noise = 0.7 + Math.random() * 0.6;
    pos.setXYZ(i, x * noise, y * noise, z * noise);
  }
  geo.computeVertexNormals();
  const mat = new THREE.MeshStandardMaterial({
    color,
    emissive: 0xa78bfa,
    emissiveIntensity: 0.05,
    flatShading: true,
    roughness: 0.8,
    metalness: 0.2,
  });
  return new THREE.Mesh(geo, mat);
};

/* ── Station mesh ── */
export const createStation = function (size = 2, color = 0x22d3ee) {
  const group = new THREE.Group();
  // Main body
  const bodyGeo = new THREE.BoxGeometry(size * 2, size, size);
  const mat = new THREE.MeshStandardMaterial({ color, emissive: color, emissiveIntensity: 0.1, metalness: 0.7, roughness: 0.3 });
  const body = new THREE.Mesh(bodyGeo, mat);
  group.add(body);
  // Cross bar
  const crossGeo = new THREE.BoxGeometry(size, size * 0.3, size * 2);
  const cross = new THREE.Mesh(crossGeo, mat);
  group.add(cross);
  // Glow
  const glow = createGlowSprite(color, size * 3);
  glow.position.z = -size;
  group.add(glow);
  return group;
};

/* ── Connection line (for star map) ── */
export const createConnectionLine = function (from, to, color = 0x1c2a3f, opacity = 0.6) {
  const points = [
    new THREE.Vector3(from.x, from.y, from.z || 0),
    new THREE.Vector3(to.x, to.y, to.z || 0),
  ];
  const geo = new THREE.BufferGeometry().setFromPoints(points);
  const mat = new THREE.LineBasicMaterial({ color, transparent: true, opacity });
  return new THREE.Line(geo, mat);
};

/* ── Raycaster for mouse picking ── */
export const raycast = function (mouse, camera, objects) {
  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(mouse, camera);
  return raycaster.intersectObjects(objects, true);
};

/* ── Orbit camera (simple, no OrbitControls dependency) ── */
export const createOrbitCamera = function (target, distance = 50, angle = Math.PI / 4) {
  const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 5000);
  camera.position.set(
    target.x + Math.cos(angle) * distance,
    target.y + Math.sin(angle) * distance * 0.5,
    target.z + Math.sin(angle) * distance
  );
  camera.lookAt(target);
  return camera;
};

/* ── Enhanced camera controller (orbit + pan + smooth fly-to) ── */
export const OrbitController = function (camera, domElement, target = new THREE.Vector3()) {
  this.camera = camera;
  this.target = target.clone();
  this.distance = camera.position.distanceTo(target);
  this.theta = Math.atan2(camera.position.x - target.x, camera.position.z - target.z);
  this.phi = Math.acos(Math.min(1, Math.max(-1, (camera.position.y - target.y) / Math.max(0.001, this.distance))));

  // Configuration
  this.damping = 0.92;
  this.dampingFactor = 0.06;
  this.rotateSpeed = 0.4;
  this.zoomSpeed = 0.8;
  this.panSpeed = 0.5;
  this.minDistance = 5;
  this.maxDistance = 500;
  this.minPolarAngle = 0.1;
  this.maxPolarAngle = Math.PI - 0.1;
  this.enablePan = true;
  this.panButton = 2; // right-click

  // State
  this.isDragging = false;
  this.isPanning = false;
  this.lastMouse = { x: 0, y: 0 };
  this.velocity = { theta: 0, phi: 0 };
  this.panVelocity = { x: 0, y: 0 };

  // Fly-to animation state
  this._flyActive = false;
  this._flyStart = { theta: 0, phi: 0, dist: 0, tx: 0, ty: 0, tz: 0 };
  this._flyEnd = { theta: 0, phi: 0, dist: 0, tx: 0, ty: 0, tz: 0 };
  this._flyT = 0;
  this._flyDuration = 1.0; // seconds

  const self = this;

  const _ctxMenuHandler = function(e) { e.preventDefault(); };

  const onDown = (e) => {
    // Cancel fly-to on user interaction
    self._flyActive = false;
    if (e.button === self.panButton && self.enablePan) {
      self.isPanning = true;
    } else if (e.button === 0) {
      self.isDragging = true;
    }
    self.lastMouse = { x: e.clientX, y: e.clientY };
  };

  const onUp = () => {
    self.isDragging = false;
    self.isPanning = false;
  };

  const onMove = (e) => {
    const dx = e.clientX - self.lastMouse.x;
    const dy = e.clientY - self.lastMouse.y;
    self.lastMouse = { x: e.clientX, y: e.clientY };

    if (self.isDragging) {
      self.velocity.theta -= dx * 0.005 * self.rotateSpeed;
      self.velocity.phi += dy * 0.005 * self.rotateSpeed;
    }

    if (self.isPanning && self.enablePan) {
      // Pan in the camera's local X/Y plane
      const panOffset = new THREE.Vector3();
      const right = new THREE.Vector3();
      const up = new THREE.Vector3(0, 1, 0);
      right.crossVectors(self.camera.getWorldDirection(new THREE.Vector3()), up).normalize();
      const actualUp = new THREE.Vector3().crossVectors(right, self.camera.getWorldDirection(new THREE.Vector3())).normalize();

      const factor = self.distance * 0.002 * self.panSpeed;
      panOffset.addScaledVector(right, -dx * factor);
      panOffset.addScaledVector(actualUp, dy * factor);

      self.target.add(panOffset);
      self.panVelocity.x = -dx * factor * 0.3;
      self.panVelocity.y = dy * factor * 0.3;
    }
  };

  const onWheel = (e) => {
    self._flyActive = false;
    const delta = e.deltaY * 0.05 * self.zoomSpeed;
    // Exponential zoom so it feels smooth at all distances
    self.distance *= (1 + delta / self.distance * 2);
    self.distance = Math.max(self.minDistance, Math.min(self.maxDistance, self.distance));
  };

  // Register event listeners
  domElement.addEventListener('mousedown', onDown);
  domElement.addEventListener('mouseup', onUp);
  domElement.addEventListener('mouseleave', onUp);
  domElement.addEventListener('mousemove', onMove);
  domElement.addEventListener('wheel', onWheel, { passive: false });
  domElement.addEventListener('contextmenu', _ctxMenuHandler);

  // Smooth fly-to a world position
  this.flyTo = function (x, y, z, dist) {
    this._flyStart = {
      theta: this.theta,
      phi: this.phi,
      dist: this.distance,
      tx: this.target.x,
      ty: this.target.y,
      tz: this.target.z,
    };
    const dx = x - this.target.x;
    const dz = z - this.target.z;
    const dy = y - this.target.y;
    this._flyEnd = {
      theta: Math.atan2(dx, dz),
      phi: Math.PI / 3, // 60° — good overhead-ish angle
      dist: dist || Math.min(60, this.distance * 0.6),
      tx: x,
      ty: y,
      tz: z,
    };
    this._flyT = 0;
    this._flyActive = true;
  };

  // Easing function (cubic ease-in-out)
  const easeInOut = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;

  this.update = function (dt) {
    // Fly-to animation
    if (this._flyActive) {
      this._flyT += (dt || 0.016) / this._flyDuration;
      if (this._flyT >= 1) {
        this._flyT = 1;
        this._flyActive = false;
      }
      const t = easeInOut(this._flyT);
      this.theta = this._flyStart.theta + (this._flyEnd.theta - this._flyStart.theta) * t;
      this.phi = this._flyStart.phi + (this._flyEnd.phi - this._flyStart.phi) * t;
      this.distance = this._flyStart.dist + (this._flyEnd.dist - this._flyStart.dist) * t;
      this.target.x = this._flyStart.tx + (this._flyEnd.tx - this._flyStart.tx) * t;
      this.target.y = this._flyStart.ty + (this._flyEnd.ty - this._flyStart.ty) * t;
      this.target.z = this._flyStart.tz + (this._flyEnd.tz - this._flyStart.tz) * t;
    } else {
      // Damped rotation
      if (!this.isDragging) {
        this.velocity.theta *= this.damping;
        this.velocity.phi *= this.damping;
      }
      this.theta += this.velocity.theta;
      this.phi += this.velocity.phi;

      // Damped pan
      if (!this.isPanning) {
        this.target.x += this.panVelocity.x;
        this.target.y += this.panVelocity.y;
        this.panVelocity.x *= this.damping;
        this.panVelocity.y *= this.damping;
      }
    }

    // Clamp polar angle
    this.phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.phi));

    // Clamp distance
    this.distance = Math.max(this.minDistance, Math.min(this.maxDistance, this.distance));

    this.camera.position.set(
      this.target.x + Math.sin(this.theta) * Math.sin(this.phi) * this.distance,
      this.target.y + Math.cos(this.phi) * this.distance,
      this.target.z + Math.cos(this.theta) * Math.sin(this.phi) * this.distance
    );
    this.camera.lookAt(this.target);
  };

  this.dispose = function () {
    domElement.removeEventListener('mousedown', onDown);
    domElement.removeEventListener('mouseup', onUp);
    domElement.removeEventListener('mouseleave', onUp);
    domElement.removeEventListener('mousemove', onMove);
    domElement.removeEventListener('wheel', onWheel);
    domElement.removeEventListener('contextmenu', _ctxMenuHandler);
  };
};

/* ── Lighting setup for space scenes ── */
export const setupSpaceLighting = function (scene) {
  const ambient = new THREE.AmbientLight(0x223344, 0.4);
  scene.add(ambient);
  const dir = new THREE.DirectionalLight(0xffffff, 0.6);
  dir.position.set(50, 100, 50);
  scene.add(dir);
  const fill = new THREE.DirectionalLight(0x22d3ee, 0.15);
  fill.position.set(-50, -30, -50);
  scene.add(fill);
};

/* ── Warp/dashed line (for routes) ── */
export const createRouteLine = function (points, color = 0x22d3ee) {
  const geo = new THREE.BufferGeometry().setFromPoints(
    points.map(p => new THREE.Vector3(p.x, p.y, p.z || 0))
  );
  const mat = new THREE.LineDashedMaterial({
    color,
    dashSize: 3,
    gapSize: 1.5,
    transparent: true,
    opacity: 0.6,
  });
  const line = new THREE.Line(geo, mat);
  line.computeLineDistances();
  return line;
};

/* ── Update label text (recreates canvas texture) ── */
export const updateLabelText = function (sprite, text, color = '#d4dce8', fontSize = 24) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  ctx.font = `${fontSize}px JetBrains Mono, ui-monospace, monospace`;
  const metrics = ctx.measureText(text);
  const width = Math.ceil(metrics.width) + 16;
  const height = fontSize + 12;
  canvas.width = width;
  canvas.height = height;
  ctx.font = `${fontSize}px JetBrains Mono, ui-monospace, monospace`;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = color;
  ctx.fillText(text, width / 2, height / 2);
  if (sprite.material.map) sprite.material.map.dispose();
  sprite.material.map = new THREE.CanvasTexture(canvas);
  sprite.material.map.minFilter = THREE.LinearFilter;
  const aspect = width / height;
  sprite.scale.set(aspect * 1.2, 1.2, 1);
  sprite.material.needsUpdate = true;
};

/* ── Star system sphere (for starmap) ── */
export const createStarSystem = function (system, scale = 1) {
  const group = new THREE.Group();
  group.userData = system;

  // Core sphere
  const coreGeo = new THREE.SphereGeometry(1.5 * scale, 12, 12);
  const coreMat = new THREE.MeshBasicMaterial({ color: new THREE.Color(system.color) });
  const core = new THREE.Mesh(coreGeo, coreMat);
  group.add(core);

  // Glow
  const glow = createGlowSprite(new THREE.Color(system.color).getHex(), 6 * scale);
  group.add(glow);

  // Label
  const label = createLabel(system.name, '#d4dce8', 20);
  label.position.y = 4 * scale;
  group.add(label);

  group.position.set(system.x * scale, system.y * scale, 0);
  return group;
};

/* ── Explosion particles ── */
export const createExplosion = function (position, color = 0xef4444, count = 20) {
  const particles = [];
  for (let i = 0; i < count; i++) {
    const geo = new THREE.SphereGeometry(0.1, 4, 4);
    const mat = new THREE.MeshBasicMaterial({ color, transparent: true, opacity: 1 });
    const p = new THREE.Mesh(geo, mat);
    p.position.copy(position);
    p.userData.velocity = new THREE.Vector3(
      (Math.random() - 0.5) * 2,
      (Math.random() - 0.5) * 2,
      (Math.random() - 0.5) * 2
    );
    p.userData.life = 1;
    particles.push(p);
  }
  return particles;
};

/* ── Camera follow (smooth) ── */
export const followTarget = function (camera, target, offset = { x: 0, y: 30, z: 30 }, smoothing = 0.05) {
  const desired = new THREE.Vector3(
    target.x + offset.x,
    target.y + offset.y,
    target.z + offset.z
  );
  camera.position.lerp(desired, smoothing);
  camera.lookAt(target);
};

/* ── Lock brackets (wireframe targeting indicator) ── */
export const createLockBrackets = function (size = 3, color = 0xf0a030) {
  const group = new THREE.Group();
  const mat = new THREE.LineBasicMaterial({ color });
  const bracketLen = size * 0.3;

  const corners = [
    { x: -size, y: size, z: size, dx: 1, dy: 0, dz: 0 }, // top-left-front
    { x: -size, y: size, z: size, dx: 0, dy: 0, dz: -1 },
    { x: size, y: size, z: size, dx: -1, dy: 0, dz: 0 },
    { x: size, y: size, z: size, dx: 0, dy: 0, dz: -1 },
    { x: -size, y: -size, z: size, dx: 1, dy: 0, dz: 0 },
    { x: -size, y: -size, z: size, dx: 0, dy: 1, dz: 0 },
    { x: size, y: -size, z: size, dx: -1, dy: 0, dz: 0 },
    { x: size, y: -size, z: size, dx: 0, dy: 1, dz: 0 },
    { x: -size, y: size, z: -size, dx: 1, dy: 0, dz: 0 },
    { x: -size, y: size, z: -size, dx: 0, dy: -1, dz: 0 },
    { x: size, y: size, z: -size, dx: -1, dy: 0, dz: 0 },
    { x: size, y: size, z: -size, dx: 0, dy: -1, dz: 0 },
    { x: -size, y: -size, z: -size, dx: 1, dy: 0, dz: 0 },
    { x: -size, y: -size, z: -size, dx: 0, dy: 1, dz: 0 },
    { x: size, y: -size, z: -size, dx: -1, dy: 0, dz: 0 },
    { x: size, y: -size, z: -size, dx: 0, dy: 1, dz: 0 },
  ];

  corners.forEach(c => {
    const points = [
      new THREE.Vector3(c.x, c.y, c.z),
      new THREE.Vector3(c.x + c.dx * bracketLen, c.y + c.dy * bracketLen, c.z + c.dz * bracketLen),
    ];
    const geo = new THREE.BufferGeometry().setFromPoints(points);
    const line = new THREE.Line(geo, mat);
    group.add(line);
  });

  return group;
};

/* ═══════════════════════════════════════════
   Orbital Physics System
   ═══════════════════════════════════════════ */

/* ── Orbital body — Keplerian motion ── */
export const OrbitalBody = function (opts) {
  this.mesh = opts.mesh || null;
  this.parentPos = opts.parentPos ? opts.parentPos.clone() : new THREE.Vector3(0, 0, 0);
  this.orbitRadius = opts.orbitRadius || 10;
  this.eccentricity = Math.min(opts.eccentricity || 0, 0.95);
  this.inclination = opts.inclination || 0;
  this.phase = opts.phase != null ? opts.phase : Math.random() * Math.PI * 2;
  this.period = Math.max(opts.period || 10, 0.1);
  this.angularVelocity = (2 * Math.PI) / this.period;
  this.children = [];
  this._worldPos = new THREE.Vector3();
};

OrbitalBody.prototype.update = function (dt) {
  this.phase += this.angularVelocity * dt;
  var theta = this.phase;
  var e = this.eccentricity;
  var a = this.orbitRadius;
  var denom = 1 + e * Math.cos(theta);
  if (Math.abs(denom) < 0.001) denom = 0.001;
  var r = a * (1 - e * e) / denom;
  var x = r * Math.cos(theta);
  var z = r * Math.sin(theta);
  var y = z * Math.sin(this.inclination);
  var zf = z * Math.cos(this.inclination);

  this._worldPos.set(
    this.parentPos.x + x,
    this.parentPos.y + y,
    this.parentPos.z + zf
  );

  if (this.mesh) {
    this.mesh.position.copy(this._worldPos);
    if (this.mesh.rotation) this.mesh.rotation.y += dt * 0.5;
  }

  for (var i = 0; i < this.children.length; i++) {
    this.children[i].parentPos.copy(this._worldPos);
    this.children[i].update(dt);
  }
};

OrbitalBody.prototype.getWorldPosition = function () {
  return this._worldPos.clone();
};

/* ── Planet mesh with optional atmosphere ── */
export const createPlanetMesh = function (size, color, atmosphere) {
  var group = new THREE.Group();
  var geo = new THREE.SphereGeometry(size, 24, 24);
  var mat = new THREE.MeshStandardMaterial({
    color: new THREE.Color(color), roughness: 0.7, metalness: 0.15,
  });
  group.add(new THREE.Mesh(geo, mat));
  if (atmosphere) {
    var aGeo = new THREE.SphereGeometry(size * 1.18, 24, 24);
    var aMat = new THREE.MeshBasicMaterial({
      color: new THREE.Color(atmosphere), transparent: true, opacity: 0.12, side: THREE.BackSide,
    });
    group.add(new THREE.Mesh(aGeo, aMat));
  }
  return group;
};

/* ── Ring system (Saturn-like) ── */
export const createRingSystem = function (innerR, outerR, color, opacity) {
  var geo = new THREE.RingGeometry(innerR, outerR, 64);
  geo.rotateX(-Math.PI / 2);
  var mat = new THREE.MeshBasicMaterial({
    color: new THREE.Color(color || '#d4a560'), transparent: true, opacity: opacity || 0.25, side: THREE.DoubleSide,
  });
  return new THREE.Mesh(geo, mat);
};

/* ── Orbit trail (elliptical ring) ── */
export const createOrbitTrail = function (radius, eccentricity, inclination, color, opacity) {
  var segments = 96;
  var points = [];
  var e = eccentricity || 0;
  var inc = inclination || 0;
  for (var i = 0; i <= segments; i++) {
    var theta = (i / segments) * Math.PI * 2;
    var d = 1 + e * Math.cos(theta);
    if (Math.abs(d) < 0.001) d = 0.001;
    var r = radius * (1 - e * e) / d;
    var x = r * Math.cos(theta);
    var z = r * Math.sin(theta);
    points.push(new THREE.Vector3(x, z * Math.sin(inc), z * Math.cos(inc)));
  }
  var geo = new THREE.BufferGeometry().setFromPoints(points);
  var mat = new THREE.LineBasicMaterial({
    color: color || 0x1c3a5f, transparent: true, opacity: opacity || 0.2,
  });
  return new THREE.Line(geo, mat);
};

/* ── Asteroid belt (orbital particle ring) ── */
export const createAsteroidBelt = function (innerRadius, outerRadius, count, opts) {
  opts = opts || {};
  var positions = new Float32Array(count * 3);
  var colors = new Float32Array(count * 3);
  var orbits = [];
  for (var i = 0; i < count; i++) {
    var radius = innerRadius + Math.random() * (outerRadius - innerRadius);
    var angle = Math.random() * Math.PI * 2;
    var inc = (Math.random() - 0.5) * (opts.maxInclination || 0.15);
    var ecc = Math.random() * (opts.maxEccentricity || 0.08);
    var baseSpeed = (2 * Math.PI) / (opts.basePeriod || 20);
    var speed = baseSpeed * Math.pow(innerRadius / Math.max(radius, 0.1), 1.5);
    orbits.push({ radius: radius, angle: angle, inc: inc, ecc: ecc, speed: speed });
    positions[i * 3] = radius * Math.cos(angle);
    positions[i * 3 + 1] = 0;
    positions[i * 3 + 2] = radius * Math.sin(angle);
    var b = 0.3 + Math.random() * 0.4;
    colors[i * 3] = b + Math.random() * 0.1;
    colors[i * 3 + 1] = b * 0.85;
    colors[i * 3 + 2] = b * 0.7;
  }
  var geo = new THREE.BufferGeometry();
  geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
  geo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
  var mat = new THREE.PointsMaterial({
    size: opts.size || 0.35, vertexColors: true, transparent: true, opacity: 0.6,
    sizeAttenuation: true, blending: THREE.AdditiveBlending, depthWrite: false,
  });
  var pts = new THREE.Points(geo, mat);
  pts.userData.orbits = orbits;
  return pts;
};

/* ── Update asteroid belt positions ── */
export const updateAsteroidBelt = function (belt: any, dt: number, parentPos?: any) {
  var orbits = belt.userData.orbits;
  if (!orbits) return;
  var pos = belt.geometry.attributes.position.array;
  var px = parentPos ? parentPos.x : 0;
  var py = parentPos ? parentPos.y : 0;
  var pz = parentPos ? parentPos.z : 0;
  for (var i = 0; i < orbits.length; i++) {
    var o = orbits[i];
    o.angle += o.speed * dt;
    var d = 1 + o.ecc * Math.cos(o.angle);
    if (Math.abs(d) < 0.001) d = 0.001;
    var r = o.radius * (1 - o.ecc * o.ecc) / d;
    var x = r * Math.cos(o.angle);
    var z = r * Math.sin(o.angle);
    pos[i * 3] = px + x;
    pos[i * 3 + 1] = py + z * Math.sin(o.inc);
    pos[i * 3 + 2] = pz + z * Math.cos(o.inc);
  }
  belt.geometry.attributes.position.needsUpdate = true;
};

/* ── Orbital station mesh ── */
export const createOrbitalStation = function (size: number, color?: number) {
  var group = new THREE.Group();
  var mat = new THREE.MeshStandardMaterial({
    color: color || 0x22d3ee, emissive: color || 0x22d3ee,
    emissiveIntensity: 0.12, metalness: 0.7, roughness: 0.3,
  });
  group.add(new THREE.Mesh(new THREE.BoxGeometry(size * 2, size, size), mat));
  group.add(new THREE.Mesh(new THREE.BoxGeometry(size, size * 0.3, size * 2), mat));
  var panelMat = new THREE.MeshStandardMaterial({ color: 0x1a3a5c, metalness: 0.8, roughness: 0.2 });
  group.add(new THREE.Mesh(new THREE.BoxGeometry(size * 3, size * 0.04, size * 0.7), panelMat));
  return group;
};
