/**
 * Nebulae component - renders nebulae as billboards with procedural textures
 */
import { useEffect, useState, useMemo } from 'react';
import { Billboard, Text } from '@react-three/drei';
import * as THREE from 'three';
import { fetchStaticData } from '../utils/api';

interface Nebula {
  name: string;
  name_zh: string;
  type: string; // emission, planetary, supernova_remnant, dark
  distance_ly: number; // Distance in light years
  ra: number; // Right Ascension in degrees
  dec: number; // Declination in degrees
  magnitude: number;
  diameter_ly: number; // Diameter in light years
  color: string;
}

/**
 * Create a procedural nebula texture based on type
 */
function createNebulaTexture(color: string, type: string): THREE.Texture {
  const canvas = document.createElement('canvas');
  canvas.width = 256;
  canvas.height = 256;
  const ctx = canvas.getContext('2d')!;

  const centerX = canvas.width / 2;
  const centerY = canvas.height / 2;
  const radius = canvas.width / 2;

  // Parse color
  const tempColor = new THREE.Color(color);
  const r = Math.floor(tempColor.r * 255);
  const g = Math.floor(tempColor.g * 255);
  const b = Math.floor(tempColor.b * 255);

  if (type === 'emission') {
    // Emission nebulae: bright, colorful, wispy
    const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
    gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0.9)`);
    gradient.addColorStop(0.3, `rgba(${r}, ${g}, ${b}, 0.7)`);
    gradient.addColorStop(0.6, `rgba(${r}, ${g}, ${b}, 0.4)`);
    gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Add wispy clouds
    for (let i = 0; i < 30; i++) {
      const angle = Math.random() * Math.PI * 2;
      const dist = Math.random() * radius * 0.8;
      const x = centerX + Math.cos(angle) * dist;
      const y = centerY + Math.sin(angle) * dist;
      const cloudGradient = ctx.createRadialGradient(x, y, 0, x, y, 15);
      cloudGradient.addColorStop(0, `rgba(${r + 50}, ${g + 50}, ${b + 50}, 0.3)`);
      cloudGradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
      ctx.fillStyle = cloudGradient;
      ctx.fillRect(x - 15, y - 15, 30, 30);
    }
  } else if (type === 'planetary') {
    // Planetary nebulae: ring-like or spherical structure
    const innerRadius = radius * 0.3;
    const outerRadius = radius * 0.9;

    // Draw ring
    for (let r_val = innerRadius; r_val < outerRadius; r_val += 1) {
      const alpha = 1 - ((r_val - innerRadius) / (outerRadius - innerRadius));
      ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${alpha * 0.6})`;
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.arc(centerX, centerY, r_val, 0, Math.PI * 2);
      ctx.stroke();
    }

    // Add central star
    const starGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, 10);
    starGradient.addColorStop(0, `rgba(255, 255, 255, 1.0)`);
    starGradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0.5)`);
    ctx.fillStyle = starGradient;
    ctx.beginPath();
    ctx.arc(centerX, centerY, 10, 0, Math.PI * 2);
    ctx.fill();
  } else if (type === 'supernova_remnant') {
    // Supernova remnants: filamentary, expanding shell
    const gradient = ctx.createRadialGradient(centerX, centerY, radius * 0.3, centerX, centerY, radius);
    gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0)`);
    gradient.addColorStop(0.5, `rgba(${r}, ${g}, ${b}, 0.8)`);
    gradient.addColorStop(0.8, `rgba(${r}, ${g}, ${b}, 0.4)`);
    gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Add filaments
    for (let i = 0; i < 50; i++) {
      const angle = Math.random() * Math.PI * 2;
      const dist = radius * 0.5 + Math.random() * radius * 0.4;
      const x = centerX + Math.cos(angle) * dist;
      const y = centerY + Math.sin(angle) * dist;
      const length = Math.random() * 20 + 5;

      ctx.strokeStyle = `rgba(${r + 50}, ${g + 50}, ${b + 50}, ${Math.random() * 0.6})`;
      ctx.lineWidth = 1;
      ctx.beginPath();
      ctx.moveTo(x, y);
      ctx.lineTo(x + Math.cos(angle) * length, y + Math.sin(angle) * length);
      ctx.stroke();
    }
  } else if (type === 'dark') {
    // Dark nebulae: darker silhouette against background
    const gradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);
    gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0.6)`);
    gradient.addColorStop(0.5, `rgba(${r}, ${g}, ${b}, 0.4)`);
    gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Add darker patches
    for (let i = 0; i < 15; i++) {
      const x = centerX + (Math.random() - 0.5) * radius;
      const y = centerY + (Math.random() - 0.5) * radius;
      const patchGradient = ctx.createRadialGradient(x, y, 0, x, y, 20);
      patchGradient.addColorStop(0, `rgba(${r * 0.5}, ${g * 0.5}, ${b * 0.5}, 0.5)`);
      patchGradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
      ctx.fillStyle = patchGradient;
      ctx.fillRect(x - 20, y - 20, 40, 40);
    }
  }

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

/**
 * Convert RA/Dec to Cartesian coordinates for celestial sphere
 */
function raDecToCartesian(ra: number, dec: number, distance: number) {
  const raRad = (ra * Math.PI) / 180;
  const decRad = (dec * Math.PI) / 180;

  const x = distance * Math.cos(decRad) * Math.cos(raRad);
  const y = distance * Math.cos(decRad) * Math.sin(raRad);
  const z = distance * Math.sin(decRad);

  return new THREE.Vector3(x, y, z);
}

/**
 * Calculate visual size based on angular diameter
 * Nebulae are measured in light years of diameter and distance
 */
function calculateAngularSize(diameterLy: number, distanceLy: number): number {
  // Angular diameter in radians
  const angularDiameter = diameterLy / distanceLy;
  // Scale for visualization (nebulae should be visible but not too large)
  return Math.max(1.5, Math.min(8, angularDiameter * 3000));
}

export function Nebulae() {
  const [nebulae, setNebulae] = useState<Nebula[]>([]);

  useEffect(() => {
    // Load nebula data from API
    fetchStaticData('nebula')
      .then((response) => {
        // Convert API response to Nebula format
        const nebulaData = response.items.map((item) => ({
          name: item.name,
          name_zh: item.name_zh,
          type: item.data.type,
          distance_ly: item.data.distance_ly,
          ra: item.data.ra,
          dec: item.data.dec,
          magnitude: item.data.magnitude,
          diameter_ly: item.data.diameter_ly,
          color: item.data.color,
        }));
        setNebulae(nebulaData);
      })
      .catch((err) => console.error('Failed to load nebulae:', err));
  }, []);

  const nebulaData = useMemo(() => {
    return nebulae.map((nebula) => {
      // Place nebulae on celestial sphere at fixed distance for visualization
      const visualDistance = 150; // Between constellations (100) and galaxies (200)
      const position = raDecToCartesian(nebula.ra, nebula.dec, visualDistance);

      // Calculate visual size based on angular diameter
      const size = calculateAngularSize(nebula.diameter_ly, nebula.distance_ly);

      // Create procedural texture for this nebula
      const texture = createNebulaTexture(nebula.color, nebula.type);

      return {
        ...nebula,
        position,
        size,
        texture,
      };
    });
  }, [nebulae]);

  if (nebulaData.length === 0) {
    return null;
  }

  return (
    <group>
      {nebulaData.map((nebula) => (
        <group key={nebula.name}>
          <Billboard
            position={nebula.position}
            follow={true}
            lockX={false}
            lockY={false}
            lockZ={false}
          >
            {/* Nebula texture */}
            <mesh>
              <planeGeometry args={[nebula.size * 2, nebula.size * 2]} />
              <meshBasicMaterial
                map={nebula.texture}
                transparent
                opacity={nebula.type === 'dark' ? 0.5 : 0.7}
                blending={nebula.type === 'dark' ? THREE.NormalBlending : THREE.AdditiveBlending}
                depthWrite={false}
              />
            </mesh>
          </Billboard>

          {/* Nebula name label - positioned slightly outward */}
          <Billboard position={nebula.position.clone().multiplyScalar(1.02)}>
            <Text
              fontSize={1.2}
              color="#FFAADD"
              anchorX="center"
              anchorY="middle"
              outlineWidth={0.1}
              outlineColor="#000000"
            >
              {nebula.name_zh}
            </Text>
          </Billboard>
        </group>
      ))}
    </group>
  );
}
