/**
 * Stars component - renders nearby stars in 3D space
 */
import { useEffect, useState, useMemo } from 'react';
import { Billboard } from '@react-three/drei';
import * as THREE from 'three';
import { request } from '../utils/request';
import { createLabelTexture } from '../utils/labelTexture';

interface Star {
  name: string;
  name_zh: string;
  distance_ly: number;
  ra: number; // Right Ascension in degrees
  dec: number; // Declination in degrees
  magnitude: number;
  color: string;
  position: THREE.Vector3;
  size: number;
}

/**
 * Convert RA/Dec to Cartesian coordinates
 * RA: Right Ascension (0-360 degrees)
 * Dec: Declination (-90 to 90 degrees)
 * Distance: fixed distance for celestial sphere
 */
function raDecToCartesian(ra: number, dec: number, distance: number = 5000) {
  // Convert to radians
  const raRad = (ra * Math.PI) / 180;
  const decRad = (dec * Math.PI) / 180;

  // Convert to Cartesian coordinates
  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);
}

/**
 * Scale star brightness based on magnitude
 * Lower magnitude = brighter star
 */
function magnitudeToSize(magnitude: number): number {
  // Brighter stars (lower magnitude) should be slightly larger
  // But all stars should be very small compared to planets
  const normalized = Math.max(-2, Math.min(12, magnitude));
  return Math.max(5, 20 - normalized * 1.2);
}

// Sub-component for individual star to handle label texture efficiently
function StarObject({ star, geometry }: { star: Star; geometry: THREE.SphereGeometry }) {
  // Generate label texture
  const labelTexture = useMemo(() => {
    return createLabelTexture(star.name_zh, null, "", "#FFFFFF");
  }, [star.name_zh]);

  return (
    <group>
      {/* Star sphere */}
      <mesh position={star.position} geometry={geometry} scale={[star.size, star.size, star.size]}>
        <meshBasicMaterial
          color={star.color}
          transparent
          opacity={0.9}
          blending={THREE.AdditiveBlending}
        />
      </mesh>

      {/* Star glow */}
      <mesh position={star.position} geometry={geometry} scale={[star.size * 2, star.size * 2, star.size * 2]}>
        <meshBasicMaterial
          color={star.color}
          transparent
          opacity={0.2}
          blending={THREE.AdditiveBlending}
        />
      </mesh>

      {/* Star name label - positioned radially outward from star */}
      {labelTexture && (
        <Billboard position={star.position.clone().multiplyScalar(1.05)}>
          <mesh scale={[200, 100, 1]}>
            <planeGeometry />
            <meshBasicMaterial 
              map={labelTexture} 
              transparent 
              depthWrite={false}
              toneMapped={false}
            />
          </mesh>
        </Billboard>
      )}
    </group>
  );
}

export function Stars() {
  const [stars, setStars] = useState<Star[]>([]);

  useEffect(() => {
    // Load star data from API
    request.get('/celestial/static/star')
      .then((res) => {
        const data = res.data;
        // API returns { category, items: [{ id, name, name_zh, data: {...} }] }
        const starData = data.items.map((item: any) => {
          // Place all stars on a celestial sphere at fixed distance (5000 units)
          const position = raDecToCartesian(item.data.ra, item.data.dec, 5000);
          const size = magnitudeToSize(item.data.magnitude);

          return {
            name: item.name,
            name_zh: item.name_zh,
            distance_ly: item.data.distance_ly,
            ra: item.data.ra,
            dec: item.data.dec,
            magnitude: item.data.magnitude,
            color: item.data.color,
            position,
            size
          };
        });
        setStars(starData);
      })
      .catch((err) => console.error('Failed to load stars:', err));
  }, []);

  // Reuse geometry for all stars to improve performance
  const sphereGeometry = useMemo(() => new THREE.SphereGeometry(1, 16, 16), []);

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

  return (
    <group>
      {stars.map((star) => (
        <StarObject key={star.name} star={star} geometry={sphereGeometry} />
      ))}
    </group>
  );
}
