import * as THREE from "three";
import {
  ACTIVE_SYSTEM_DETAIL_SCALE,
  MOON_RENDER_SCALE,
  PLANET_RENDER_SCALE,
  STAR_RENDER_SCALE,
} from "./viewerConstants";
import type {
  CelestialSnapshot,
  ClaimSnapshot,
  ConstructionSiteSnapshot,
  MoonSnapshot,
  PlanetSnapshot,
  ResourceNodeSnapshot,
  ShipSnapshot,
  StationSnapshot,
  SystemSnapshot,
} from "./contracts";
import type { MoonVisual } from "./viewerTypes";
import {
  celestialRenderRadius,
  computeMoonLocalPosition,
  computeMoonRenderRadius,
  computePlanetLocalPosition,
  scaleLocalVector,
  starHaloOpacity,
  toThreeVector,
} from "./viewerMath";
import { createSceneNode } from "./viewerScenePrimitives";
import type { SceneNode } from "./viewerScenePrimitives";

export function createNodeMesh(node: ResourceNodeSnapshot): SceneNode {
  const isGas = node.sourceKind === "gas-cloud" || node.itemId === "gas";
  const mesh = new THREE.Mesh(
    isGas ? new THREE.SphereGeometry(18, 14, 14) : new THREE.IcosahedronGeometry(12, 0),
    new THREE.MeshStandardMaterial({
      color: isGas ? 0x7fd6ff : 0xd2b07a,
      flatShading: !isGas,
      transparent: isGas,
      opacity: isGas ? 0.68 : 1,
      emissive: new THREE.Color(isGas ? 0x7fd6ff : 0xd2b07a).multiplyScalar(isGas ? 0.22 : 0.05),
    }),
  );
  mesh.position.copy(toThreeVector(node.localPosition));
  mesh.scale.setScalar(0.8 + (node.oreRemaining / Math.max(node.maxOre, 1)) * 0.6);
  return createSceneNode(mesh);
}

export function createCelestialMesh(node: CelestialSnapshot, celestialColor: (kind: string) => string): SceneNode {
  const color = celestialColor(node.kind);
  return createSceneNode(new THREE.Mesh(
    new THREE.OctahedronGeometry(0.08, 0),
    new THREE.MeshStandardMaterial({
      color,
      emissive: new THREE.Color(color).multiplyScalar(0.16),
      roughness: 0.35,
      metalness: 0.45,
    }),
  ));
}

export function createClaimMesh(claim: ClaimSnapshot): SceneNode {
  return createSceneNode(new THREE.Mesh(
    new THREE.ConeGeometry(9, 20, 4),
    new THREE.MeshStandardMaterial({
      color: claim.state === "active" ? 0xff7f50 : 0xff5b5b,
      emissive: new THREE.Color(claim.state === "active" ? 0xff7f50 : 0xff5b5b).multiplyScalar(0.16),
      roughness: 0.4,
      metalness: 0.28,
    }),
  ));
}

export function createConstructionSiteMesh(site: ConstructionSiteSnapshot): SceneNode {
  return createSceneNode(new THREE.Mesh(
    new THREE.TorusKnotGeometry(7, 2.2, 54, 8),
    new THREE.MeshStandardMaterial({
      color: site.state === "completed" ? 0x46d37f : 0x9df29c,
      emissive: new THREE.Color(site.state === "completed" ? 0x46d37f : 0x9df29c).multiplyScalar(0.15),
      roughness: 0.34,
      metalness: 0.48,
    }),
  ));
}

export function createStarCluster(system: SystemSnapshot): SceneNode {
  const root = new THREE.Group();
  for (const [index, star] of system.stars.entries()) {
    const renderedSize = celestialRenderRadius(star.size, 0.00018, 40, 0.62);
    const offset = system.stars.length > 1
      ? (index === 0
        ? new THREE.Vector3(-renderedSize * 0.55, 0, 0)
        : new THREE.Vector3(renderedSize * 0.75, renderedSize * 0.08, 0))
      : new THREE.Vector3(0, 0, 0);
    const mesh = new THREE.Mesh(
      new THREE.SphereGeometry(renderedSize, 24, 24),
      new THREE.MeshBasicMaterial({ color: star.color }),
    );
    const halo = new THREE.Mesh(
      new THREE.SphereGeometry(renderedSize * 1.45, 20, 20),
      new THREE.MeshBasicMaterial({
        color: star.color,
        transparent: true,
        opacity: starHaloOpacity(star.kind),
        side: THREE.BackSide,
      }),
    );
    mesh.position.copy(offset);
    halo.position.copy(offset);
    root.add(mesh, halo);
  }

  return createSceneNode(root);
}


export function createPlanetRing(planet: PlanetSnapshot): SceneNode {
  const renderedPlanetRadius = celestialRenderRadius(planet.size, 0.00012, 0.03, 0.62);
  const ring = new THREE.Mesh(
    new THREE.RingGeometry(renderedPlanetRadius * 1.35, renderedPlanetRadius * 2.15, 48),
    new THREE.MeshBasicMaterial({
      color: 0xdac89a,
      transparent: true,
      opacity: 0.42,
      side: THREE.DoubleSide,
    }),
  );
  ring.rotation.x = Math.PI / 2;
  ring.rotation.z = THREE.MathUtils.degToRad(planet.orbitInclination * 0.25);
  return createSceneNode(ring);
}

function createMoonOrbit(moon: MoonSnapshot): SceneNode {
  const segments = 64;
  const period = (2 * Math.PI) / Math.max(Math.abs(moon.orbitSpeed), 1e-6);
  const points: THREE.Vector3[] = [];
  for (let i = 0; i <= segments; i++) {
    points.push(
      scaleLocalVector(computeMoonLocalPosition(moon, (i / segments) * period))
        .multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE),
    );
  }
  return createSceneNode(new THREE.LineLoop(
    new THREE.BufferGeometry().setFromPoints(points),
    new THREE.LineBasicMaterial({
      color: moon.color,
      transparent: true,
      opacity: 0,
      depthWrite: false,
      depthTest: false,
    }),
  ));
}

export function createMoonVisuals(planet: PlanetSnapshot, documentRef: Document): MoonVisual[] {
  return planet.moons.map((moon, moonIndex) => {
    const moonSize = computeMoonRenderRadius(moon);
    const mesh = new THREE.Mesh(
      new THREE.SphereGeometry(moonSize, 12, 12),
      new THREE.MeshStandardMaterial({
        color: moon.color,
        roughness: 0.96,
        metalness: 0.02,
      }),
    );
    const baseColor = new THREE.Color(moon.color);
    const hsl = { h: 0, s: 0, l: 0 };
    baseColor.getHSL(hsl);
    const iconColor = new THREE.Color().setHSL(hsl.h, Math.max(hsl.s, 0.4), 0.72).getStyle();
    const iconBaseScale = 72;
    const icon = createTacticalIcon(documentRef, iconColor, iconBaseScale);
    return {
      systemId: "",
      planetIndex: -1,
      moonIndex,
      mesh: createSceneNode(mesh),
      icon,
      iconBaseScale,
      orbit: createMoonOrbit(moon),
    };
  });
}

export function createPlanetOrbit(planet: PlanetSnapshot): SceneNode {
  const segments = 96;
  const points: THREE.Vector3[] = [];
  for (let i = 0; i <= segments; i++) {
    const phase = (i / segments) * 360;
    points.push(scaleLocalVector(computePlanetLocalPosition(planet, 0, phase)).multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE));
  }
  return createSceneNode(new THREE.LineLoop(
    new THREE.BufferGeometry().setFromPoints(points),
    new THREE.LineBasicMaterial({
      color: planet.color,
      transparent: true,
      opacity: 0.22,
      depthWrite: false,
      depthTest: false,
    }),
  ));
}

export function createStationMesh(station: StationSnapshot): SceneNode {
  const mesh = new THREE.Mesh(
    new THREE.CylinderGeometry(24, 24, 18, 10),
    new THREE.MeshStandardMaterial({ color: station.color, emissive: new THREE.Color(station.color).multiplyScalar(0.1) }),
  );
  mesh.rotation.x = Math.PI / 2;
  mesh.position.copy(toThreeVector(station.localPosition));
  return createSceneNode(mesh);
}

export function createShipMesh(ship: ShipSnapshot, size: number, length: number, color: string): SceneNode {
  const geometry = new THREE.ConeGeometry(size, length, 7);
  geometry.rotateX(Math.PI / 2);
  const mesh = new THREE.Mesh(
    geometry,
    new THREE.MeshStandardMaterial({
      color,
      emissive: new THREE.Color(color).multiplyScalar(0.18),
    }),
  );
  mesh.position.copy(toThreeVector(ship.localPosition));
  return createSceneNode(mesh);
}

function createStarGlowTexture(documentRef: Document): THREE.CanvasTexture {
  const canvas = documentRef.createElement("canvas");
  canvas.width = 128;
  canvas.height = 128;
  const context = canvas.getContext("2d");
  if (!context) {
    throw new Error("Unable to create star glow texture");
  }

  const gradient = context.createRadialGradient(64, 64, 0, 64, 64, 64);
  gradient.addColorStop(0, "rgba(255,255,255,1)");
  gradient.addColorStop(0.14, "rgba(255,255,255,0.95)");
  gradient.addColorStop(0.35, "rgba(255,255,255,0.42)");
  gradient.addColorStop(0.68, "rgba(180,205,255,0.1)");
  gradient.addColorStop(1, "rgba(0,0,0,0)");
  context.fillStyle = gradient;
  context.fillRect(0, 0, 128, 128);

  const texture = new THREE.CanvasTexture(canvas);
  texture.needsUpdate = true;
  return texture;
}

function createStarSparkleTexture(documentRef: Document): THREE.CanvasTexture {
  const canvas = documentRef.createElement("canvas");
  canvas.width = 128;
  canvas.height = 128;
  const context = canvas.getContext("2d");
  if (!context) {
    throw new Error("Unable to create star sparkle texture");
  }

  context.clearRect(0, 0, 128, 128);
  context.translate(64, 64);
  context.lineCap = "round";

  const bloom = context.createRadialGradient(0, 0, 0, 0, 0, 48);
  bloom.addColorStop(0, "rgba(255,255,255,0.95)");
  bloom.addColorStop(0.3, "rgba(255,255,255,0.24)");
  bloom.addColorStop(1, "rgba(0,0,0,0)");
  context.fillStyle = bloom;
  context.beginPath();
  context.arc(0, 0, 48, 0, Math.PI * 2);
  context.fill();

  context.strokeStyle = "rgba(255,255,255,0.75)";
  context.lineWidth = 4;
  context.beginPath();
  context.moveTo(-38, 0);
  context.lineTo(38, 0);
  context.moveTo(0, -38);
  context.lineTo(0, 38);
  context.stroke();

  context.rotate(Math.PI / 4);
  context.strokeStyle = "rgba(255,255,255,0.35)";
  context.lineWidth = 2;
  context.beginPath();
  context.moveTo(-28, 0);
  context.lineTo(28, 0);
  context.moveTo(0, -28);
  context.lineTo(0, 28);
  context.stroke();

  const texture = new THREE.CanvasTexture(canvas);
  texture.needsUpdate = true;
  return texture;
}

function sampleBackdropStarColor(): THREE.Color {
  const roll = Math.random();
  if (roll < 0.1) {
    return new THREE.Color().setHSL(0.08, THREE.MathUtils.randFloat(0.65, 0.9), THREE.MathUtils.randFloat(0.78, 0.9));
  }
  if (roll < 0.28) {
    return new THREE.Color().setHSL(0.58, THREE.MathUtils.randFloat(0.28, 0.55), THREE.MathUtils.randFloat(0.78, 0.9));
  }
  if (roll < 0.92) {
    return new THREE.Color().setHSL(0.61, THREE.MathUtils.randFloat(0.08, 0.3), THREE.MathUtils.randFloat(0.84, 0.97));
  }
  return new THREE.Color().setHSL(0.76, THREE.MathUtils.randFloat(0.25, 0.48), THREE.MathUtils.randFloat(0.78, 0.88));
}

function createStarPointLayer(radius: number, starCount: number, size: number, opacity: number): THREE.Points {
  const positions = new Float32Array(starCount * 3);
  const colors = new Float32Array(starCount * 3);

  for (let index = 0; index < starCount; index += 1) {
    const direction = new THREE.Vector3(
      THREE.MathUtils.randFloatSpread(2),
      THREE.MathUtils.randFloatSpread(2),
      THREE.MathUtils.randFloatSpread(2),
    ).normalize().multiplyScalar(radius * THREE.MathUtils.randFloat(0.83, 1));
    const color = sampleBackdropStarColor().multiplyScalar(THREE.MathUtils.randFloat(0.55, 1.2));

    positions[index * 3] = direction.x;
    positions[index * 3 + 1] = direction.y;
    positions[index * 3 + 2] = direction.z;
    colors[index * 3] = color.r;
    colors[index * 3 + 1] = color.g;
    colors[index * 3 + 2] = color.b;
  }

  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));

  return new THREE.Points(
    geometry,
    new THREE.PointsMaterial({
      size,
      sizeAttenuation: false,
      vertexColors: true,
      transparent: true,
      opacity,
      depthWrite: false,
      blending: THREE.AdditiveBlending,
      fog: false,
    }),
  );
}

export function createBackdropStars(documentRef: Document): THREE.Group {
  const radius = 36000;
  const root = new THREE.Group();

  root.add(
    createStarPointLayer(radius, 2800, 1.15, 0.5),
    createStarPointLayer(radius, 900, 1.9, 0.85),
    createStarPointLayer(radius, 240, 3.1, 0.95),
  );

  const glowTexture = createStarGlowTexture(documentRef);
  const sparkleTexture = createStarSparkleTexture(documentRef);
  for (let index = 0; index < 72; index += 1) {
    const direction = new THREE.Vector3(
      THREE.MathUtils.randFloatSpread(2),
      THREE.MathUtils.randFloatSpread(2),
      THREE.MathUtils.randFloatSpread(2),
    ).normalize().multiplyScalar(radius * THREE.MathUtils.randFloat(0.84, 0.98));
    const color = sampleBackdropStarColor().multiplyScalar(THREE.MathUtils.randFloat(0.9, 1.45));
    const glow = new THREE.Sprite(new THREE.SpriteMaterial({
      map: glowTexture,
      color,
      transparent: true,
      opacity: THREE.MathUtils.randFloat(0.5, 0.95),
      depthWrite: false,
      blending: THREE.AdditiveBlending,
      fog: false,
    }));
    const sparkle = new THREE.Sprite(new THREE.SpriteMaterial({
      map: sparkleTexture,
      color: color.clone().lerp(new THREE.Color(0xffffff), 0.35),
      transparent: true,
      opacity: THREE.MathUtils.randFloat(0.2, 0.55),
      depthWrite: false,
      blending: THREE.AdditiveBlending,
      fog: false,
    }));
    const glowScale = THREE.MathUtils.randFloat(120, 260);
    glow.position.copy(direction);
    glow.scale.set(glowScale, glowScale, 1);
    sparkle.position.copy(direction);
    sparkle.material.rotation = THREE.MathUtils.randFloat(0, Math.PI);
    sparkle.scale.set(glowScale * THREE.MathUtils.randFloat(0.9, 1.4), glowScale * THREE.MathUtils.randFloat(0.9, 1.4), 1);
    root.add(glow, sparkle);
  }

  return root;
}

export function createPlanetTexture(color: string, seed: number, documentRef: Document): THREE.CanvasTexture {
  const W = 256, H = 128;
  const canvas = documentRef.createElement("canvas");
  canvas.width = W;
  canvas.height = H;
  const ctx = canvas.getContext("2d");
  if (!ctx) throw new Error("Unable to create planet texture");

  const imageData = ctx.createImageData(W, H);
  const base = new THREE.Color(color);

  function hash(x: number, y: number): number {
    const n = Math.sin(x * 127.1 + y * 311.7 + seed * 74.3) * 43758.5453;
    return n - Math.floor(n);
  }

  function smoothNoise(x: number, y: number): number {
    const ix = Math.floor(x), iy = Math.floor(y);
    const fx = x - ix, fy = y - iy;
    const ux = fx * fx * (3 - 2 * fx), uy = fy * fy * (3 - 2 * fy);
    const a = hash(ix, iy), b = hash(ix + 1, iy);
    const c = hash(ix, iy + 1), d = hash(ix + 1, iy + 1);
    return a + (b - a) * ux + (c - a) * uy + (a - b - c + d) * ux * uy;
  }

  function fbm(x: number, y: number): number {
    let v = 0, amp = 0.5, freq = 1;
    for (let i = 0; i < 5; i++) { v += smoothNoise(x * freq, y * freq) * amp; amp *= 0.5; freq *= 2; }
    return v;
  }

  for (let y = 0; y < H; y++) {
    for (let x = 0; x < W; x++) {
      const nx = (x / W) * 5, ny = (y / H) * 3;
      const turb = fbm(nx + 0.1, ny + 0.1) - 0.5;
      const band = Math.sin((y / H * 10 + turb * 3) * Math.PI);
      const light = 0.62 + band * 0.38;
      const idx = (y * W + x) * 4;
      imageData.data[idx] = Math.min(255, base.r * 255 * light);
      imageData.data[idx + 1] = Math.min(255, base.g * 255 * light);
      imageData.data[idx + 2] = Math.min(255, base.b * 255 * light);
      imageData.data[idx + 3] = 255;
    }
  }

  ctx.putImageData(imageData, 0, 0);
  const texture = new THREE.CanvasTexture(canvas);
  texture.wrapS = THREE.RepeatWrapping;
  texture.needsUpdate = true;
  return texture;
}

export function createNebulaTexture(documentRef: Document): THREE.CanvasTexture {
  const canvas = documentRef.createElement("canvas");
  canvas.width = 512;
  canvas.height = 512;
  const context = canvas.getContext("2d");
  if (!context) {
    throw new Error("Unable to create nebula texture");
  }

  const palettes = [
    ["rgba(80,220,255,0.24)", "rgba(120,110,255,0.18)", "rgba(255,255,255,0.14)"],
    ["rgba(255,130,205,0.24)", "rgba(110,170,255,0.16)", "rgba(255,240,255,0.12)"],
    ["rgba(120,255,205,0.2)", "rgba(100,160,255,0.18)", "rgba(255,255,255,0.1)"],
  ];

  context.clearRect(0, 0, 512, 512);

  for (let layer = 0; layer < palettes.length; layer += 1) {
    for (let index = 0; index < 18; index += 1) {
      const x = THREE.MathUtils.randFloat(40, 472);
      const y = THREE.MathUtils.randFloat(40, 472);
      const radius = THREE.MathUtils.randFloat(55, 180);
      const [core, mid, edge] = palettes[layer];
      const puff = context.createRadialGradient(x, y, 0, x, y, radius);
      puff.addColorStop(0, core);
      puff.addColorStop(0.4, mid);
      puff.addColorStop(0.78, edge);
      puff.addColorStop(1, "rgba(0,0,0,0)");
      context.fillStyle = puff;
      context.beginPath();
      context.arc(x, y, radius, 0, Math.PI * 2);
      context.fill();
    }
  }

  for (let index = 0; index < 36; index += 1) {
    const x = THREE.MathUtils.randFloat(50, 462);
    const y = THREE.MathUtils.randFloat(50, 462);
    const radius = THREE.MathUtils.randFloat(18, 60);
    const glow = context.createRadialGradient(x, y, 0, x, y, radius);
    glow.addColorStop(0, "rgba(255,255,255,0.12)");
    glow.addColorStop(0.4, "rgba(255,255,255,0.05)");
    glow.addColorStop(1, "rgba(0,0,0,0)");
    context.fillStyle = glow;
    context.beginPath();
    context.arc(x, y, radius, 0, Math.PI * 2);
    context.fill();
  }

  // Feather the entire texture toward the borders so large sprites do not show a card-like cutoff.
  const edgeFade = context.createRadialGradient(256, 256, 86, 256, 256, 256);
  edgeFade.addColorStop(0, "rgba(255,255,255,1)");
  edgeFade.addColorStop(0.58, "rgba(255,255,255,0.96)");
  edgeFade.addColorStop(0.82, "rgba(255,255,255,0.42)");
  edgeFade.addColorStop(1, "rgba(255,255,255,0)");
  context.globalCompositeOperation = "destination-in";
  context.fillStyle = edgeFade;
  context.fillRect(0, 0, 512, 512);
  context.globalCompositeOperation = "source-over";

  const texture = new THREE.CanvasTexture(canvas);
  texture.needsUpdate = true;
  return texture;
}

export function createNebulaClouds(texture: THREE.Texture): THREE.Sprite[] {
  const seeds = [
    { direction: new THREE.Vector3(0.76, 0.28, -0.58), color: "#5bd4ff", scale: 24000, opacity: 0.22, rotation: 0.18 },
    { direction: new THREE.Vector3(0.7, 0.34, -0.54), color: "#93b3ff", scale: 18000, opacity: 0.16, rotation: -0.22 },
    { direction: new THREE.Vector3(-0.58, 0.24, -0.78), color: "#ff8cc6", scale: 22000, opacity: 0.2, rotation: 0.34 },
    { direction: new THREE.Vector3(-0.48, 0.14, -0.86), color: "#8a8dff", scale: 16000, opacity: 0.14, rotation: -0.4 },
    { direction: new THREE.Vector3(0.24, -0.46, -0.85), color: "#79ffd6", scale: 20000, opacity: 0.17, rotation: 0.52 },
    { direction: new THREE.Vector3(-0.34, 0.58, 0.74), color: "#79b7ff", scale: 26000, opacity: 0.16, rotation: -0.12 },
  ];

  return seeds.map((seed, index) => {
    const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
      map: texture,
      transparent: true,
      opacity: seed.opacity,
      depthWrite: false,
      color: seed.color,
      blending: THREE.AdditiveBlending,
      fog: false,
    }));
    sprite.position.copy(seed.direction.normalize().multiplyScalar(23000 + index * 1800));
    sprite.material.rotation = seed.rotation;
    sprite.scale.set(seed.scale, seed.scale * THREE.MathUtils.randFloat(0.52, 0.78), 1);
    return sprite;
  });
}

export function createTacticalIcon(documentRef: Document, color: string, size: number): SceneNode {
  const canvas = documentRef.createElement("canvas");
  canvas.width = 64;
  canvas.height = 64;
  const context = canvas.getContext("2d");
  if (!context) {
    throw new Error("Unable to create tactical icon");
  }

  context.clearRect(0, 0, 64, 64);
  context.strokeStyle = color;
  context.lineWidth = 5;
  context.beginPath();
  context.arc(32, 32, 18, 0, Math.PI * 2);
  context.stroke();

  const texture = new THREE.CanvasTexture(canvas);
  const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
    map: texture,
    transparent: true,
    depthWrite: false,
    depthTest: false,
    color: "#ffffff",
  }));
  sprite.scale.setScalar(size);
  sprite.visible = false;
  return createSceneNode(sprite);
}

export function createShipTacticalIcon(documentRef: Document, color: string, size: number): SceneNode {
  const canvas = documentRef.createElement("canvas");
  canvas.width = 128;
  canvas.height = 192;
  const context = canvas.getContext("2d");
  if (!context) {
    throw new Error("Unable to create ship tactical icon");
  }

  context.clearRect(0, 0, canvas.width, canvas.height);
  context.lineCap = "round";
  context.lineJoin = "round";
  context.strokeStyle = color;
  context.fillStyle = "rgba(7, 16, 30, 0.8)";
  context.lineWidth = 6;

  context.globalAlpha = 0.92;
  context.beginPath();
  context.moveTo(64, 182);
  context.lineTo(64, 108);
  context.stroke();

  context.globalAlpha = 1;
  context.beginPath();
  context.arc(64, 70, 26, 0, Math.PI * 2);
  context.fill();
  context.stroke();

  context.beginPath();
  context.moveTo(64, 34);
  context.lineTo(64, 49);
  context.stroke();

  context.beginPath();
  context.moveTo(64, 91);
  context.lineTo(64, 106);
  context.stroke();

  context.beginPath();
  context.moveTo(28, 70);
  context.lineTo(43, 70);
  context.stroke();

  context.beginPath();
  context.moveTo(85, 70);
  context.lineTo(100, 70);
  context.stroke();

  context.beginPath();
  context.moveTo(44, 116);
  context.lineTo(64, 136);
  context.lineTo(84, 116);
  context.stroke();

  const texture = new THREE.CanvasTexture(canvas);
  const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
    map: texture,
    transparent: true,
    depthWrite: false,
    depthTest: false,
    color: "#ffffff",
    fog: false,
  }));
  sprite.center.set(0.5, 0.08);
  sprite.scale.set(size * 1.2, size * 1.8, 1);
  sprite.visible = false;
  return createSceneNode(sprite);
}


export function createStarDot(documentRef: Document, color: string): SceneNode {
  const canvas = documentRef.createElement("canvas");
  canvas.width = 32;
  canvas.height = 32;
  const context = canvas.getContext("2d");
  if (!context) {
    throw new Error("Unable to create star dot canvas");
  }

  context.clearRect(0, 0, 32, 32);
  context.fillStyle = color;
  context.beginPath();
  context.arc(16, 16, 12, 0, Math.PI * 2);
  context.fill();

  const texture = new THREE.CanvasTexture(canvas);
  const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
    map: texture,
    transparent: true,
    depthWrite: false,
    depthTest: false,
    color: "#ffffff",
    fog: false,
  }));
  sprite.scale.setScalar(4);
  sprite.visible = false;
  return createSceneNode(sprite);
}

export function createShellReticle(documentRef: Document, color: string, size: number): SceneNode {
  const canvas = documentRef.createElement("canvas");
  canvas.width = 128;
  canvas.height = 128;
  const context = canvas.getContext("2d");
  if (!context) {
    throw new Error("Unable to create shell reticle");
  }

  context.clearRect(0, 0, 128, 128);
  context.strokeStyle = color;
  context.lineWidth = 6;
  context.globalAlpha = 0.58;
  context.beginPath();
  context.arc(64, 64, 48, 0.12 * Math.PI, 0.34 * Math.PI);
  context.stroke();
  context.beginPath();
  context.arc(64, 64, 48, 0.62 * Math.PI, 0.84 * Math.PI);
  context.stroke();
  context.beginPath();
  context.arc(64, 64, 48, 1.12 * Math.PI, 1.34 * Math.PI);
  context.stroke();
  context.beginPath();
  context.arc(64, 64, 48, 1.62 * Math.PI, 1.84 * Math.PI);
  context.stroke();

  const texture = new THREE.CanvasTexture(canvas);
  const material = new THREE.SpriteMaterial({
    map: texture,
    transparent: true,
    depthWrite: false,
    depthTest: false,
    color,
    opacity: 1,
    blending: THREE.AdditiveBlending,
    fog: false,
  });
  const sprite = createSceneNode(new THREE.Sprite(material));
  sprite.setScaleScalar(size);
  sprite.setVisible(false);
  sprite.setRenderOrder(1000);
  return sprite;
}
