import * as THREE from 'three';
import { Rng, fbm2 } from './noise.js';
import { createPlanetMesh } from './planet.js';

function softSprite() {
  const c = document.createElement('canvas');
  c.width = c.height = 64;
  const ctx = c.getContext('2d');
  const g = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
  g.addColorStop(0, 'rgba(255,255,255,1)');
  g.addColorStop(0.25, 'rgba(255,255,255,0.85)');
  g.addColorStop(0.6, 'rgba(255,255,255,0.18)');
  g.addColorStop(1, 'rgba(255,255,255,0)');
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, 64, 64);
  const t = new THREE.CanvasTexture(c);
  t.colorSpace = THREE.SRGBColorSpace;
  return t;
}

function nebulaTexture(seed) {
  const c = document.createElement('canvas');
  c.width = 1024; c.height = 512;
  const ctx = c.getContext('2d');
  const img = ctx.createImageData(c.width, c.height);
  const a = new THREE.Color(0x05060f), b = new THREE.Color(0x1a1140), cc = new THREE.Color(0x06243f);
  const d = new THREE.Color(0x3a1252);
  const tmp = new THREE.Color();
  for (let y = 0; y < c.height; y++) {
    for (let x = 0; x < c.width; x++) {
      const u = x / c.width, v = y / c.height;
      let n = fbm2(u * 4, v * 4, seed, 6) * 0.5 + 0.5;
      let n2 = fbm2(u * 9 + 11, v * 9 + 7, seed + 50, 5) * 0.5 + 0.5;
      tmp.copy(a).lerp(b, Math.pow(n, 1.6)).lerp(cc, Math.pow(n2, 2.0) * 0.7).lerp(d, Math.pow(n * n2, 2.4));
      const i = (y * c.width + x) * 4;
      img.data[i] = tmp.r * 255; img.data[i + 1] = tmp.g * 255; img.data[i + 2] = tmp.b * 255; img.data[i + 3] = 255;
    }
  }
  ctx.putImageData(img, 0, 0);
  // sprinkle distant stars onto the nebula
  const rng = new Rng(seed + 999);
  for (let i = 0; i < 1400; i++) {
    const x = rng.next() * c.width, y = rng.next() * c.height;
    const s = rng.range(0.4, 1.6);
    ctx.fillStyle = `rgba(255,255,255,${rng.range(0.15, 0.8)})`;
    ctx.fillRect(x, y, s, s);
  }
  const t = new THREE.CanvasTexture(c);
  t.colorSpace = THREE.SRGBColorSpace;
  return t;
}

export function createSpace(specs, systemSeed) {
  const scene = new THREE.Scene();
  const rng = new Rng(systemSeed + 7);

  // --- nebula skybox ---
  const neb = new THREE.Mesh(
    new THREE.SphereGeometry(6000, 32, 24),
    new THREE.MeshBasicMaterial({ map: nebulaTexture(systemSeed), side: THREE.BackSide, fog: false })
  );
  neb.name = 'nebula';
  scene.add(neb);

  // --- starfield (two layers, colored) ---
  const sprite = softSprite();
  const starGroup = new THREE.Group();
  for (let layer = 0; layer < 2; layer++) {
    const count = layer === 0 ? 2600 : 900;
    const rad = layer === 0 ? 3200 : 2200;
    const pos = new Float32Array(count * 3);
    const col = new Float32Array(count * 3);
    const cTmp = new THREE.Color();
    for (let i = 0; i < count; i++) {
      const v = new THREE.Vector3().randomDirection().multiplyScalar(rad * rng.range(0.7, 1));
      pos[i * 3] = v.x; pos[i * 3 + 1] = v.y; pos[i * 3 + 2] = v.z;
      const h = rng.chance(0.7) ? rng.range(0.5, 0.65) : rng.range(0.02, 0.12);
      cTmp.setHSL(h, rng.range(0.1, 0.6), rng.range(0.7, 1));
      col[i * 3] = cTmp.r; col[i * 3 + 1] = cTmp.g; col[i * 3 + 2] = cTmp.b;
    }
    const g = new THREE.BufferGeometry();
    g.setAttribute('position', new THREE.BufferAttribute(pos, 3));
    g.setAttribute('color', new THREE.BufferAttribute(col, 3));
    const m = new THREE.PointsMaterial({
      size: layer === 0 ? 7 : 13, map: sprite, vertexColors: true, transparent: true,
      depthWrite: false, blending: THREE.AdditiveBlending, sizeAttenuation: true, fog: false,
    });
    starGroup.add(new THREE.Points(g, m));
  }
  scene.add(starGroup);

  // --- sun ---
  const sunDir = new THREE.Vector3(-0.5, 0.28, -0.82).normalize();
  const sunDist = 4200;
  const sunPos = sunDir.clone().multiplyScalar(sunDist);
  const sunMesh = new THREE.Mesh(
    new THREE.SphereGeometry(120, 32, 24),
    new THREE.MeshBasicMaterial({ color: 0xfff2d0, fog: false })
  );
  sunMesh.position.copy(sunPos);
  scene.add(sunMesh);
  const sunGlow = new THREE.Sprite(new THREE.SpriteMaterial({
    map: softSprite(), color: 0xffd98a, transparent: true, blending: THREE.AdditiveBlending,
    depthWrite: false, fog: false,
  }));
  sunGlow.scale.setScalar(1400);
  sunGlow.position.copy(sunPos);
  scene.add(sunGlow);

  const sunLight = new THREE.DirectionalLight(0xfff0d8, 2.4);
  sunLight.position.copy(sunPos);
  scene.add(sunLight);
  scene.add(new THREE.AmbientLight(0x3a4a66, 0.5));
  const fill = new THREE.HemisphereLight(0x223a66, 0x080610, 0.6);
  scene.add(fill);

  // --- planets ---
  const planets = [];
  const angBase = rng.range(0, Math.PI * 2);
  specs.forEach((spec, i) => {
    const radius = 120 + i * 22;
    const mesh = createPlanetMesh(spec, radius);
    const ang = angBase + i * (Math.PI * 2 / specs.length) + rng.range(-0.2, 0.2);
    const dist = 700 + i * 520;
    mesh.position.set(Math.cos(ang) * dist, rng.range(-120, 160), Math.sin(ang) * dist);
    mesh.rotation.z = rng.range(-0.3, 0.3);
    scene.add(mesh);
    planets.push({ mesh, spec, radius, pos: mesh.position });
  });

  scene.fog = null; // space has no fog

  function update(dt) {
    for (const p of planets) {
      p.mesh.userData.surf.rotation.y += p.spec.rotSpeed * dt;
      if (p.mesh.userData.clouds) p.mesh.userData.clouds.rotation.y += p.spec.rotSpeed * 1.3 * dt;
    }
  }

  // nearest planet within an approach threshold (relative to planet radius)
  function getApproach(shipPos) {
    let best = null, bestD = Infinity;
    for (const p of planets) {
      const d = shipPos.distanceTo(p.pos);
      if (d < bestD) { bestD = d; best = p; }
    }
    const thresh = best ? best.radius * 2.7 : 0;
    return { planet: best, dist: bestD, inRange: bestD < thresh, thresh };
  }

  return { scene, planets, sunLight, sunMesh, sunPos, sunDir, starGroup, update, getApproach };
}
