import { useRef, type ReactNode } from "react";
import { Billboard, Line, Text } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import type { ThreeEvent } from "@react-three/fiber";
import type * as THREE from "three";
import type { SystemPlanet, SystemPointOfInterest } from "./types";
import { getOrbitalSpeed, getSystemPoiOrbitSpec, hashString } from "./poiOrbit";
export { getSystemPoiPosition } from "./poiOrbit";

type StarSystemContentsProps = {
  systemId: string;
  systemName: string;
  starColor: string;
  planets: SystemPlanet[];
  pointsOfInterest: SystemPointOfInterest[];
  selectedPoiId?: string | null;
  expanded?: boolean;
  scale?: number;
  onPoiSelect?: (poi: SystemPointOfInterest, event: ThreeEvent<MouseEvent>) => void;
  onPoiContext?: (poi: SystemPointOfInterest, event: ThreeEvent<MouseEvent>) => void;
};

const poiColors: Record<SystemPointOfInterest["type"], string> = {
  station: "#22d3ee",
  asteroid_belt: "#94a3b8",
  anomaly: "#f0a030",
  stargate: "#a78bfa",
  site: "#22c55e",
};

const planetColors: Record<string, string> = {
  Barren: "#8a7a6a",
  Gas: "#d4a560",
  "Gas Giant": "#d4a560",
  Ice: "#93c5fd",
  Lava: "#ef4444",
  Oceanic: "#38bdf8",
  Rocky: "#a8a29e",
  Terrestrial: "#22c55e",
};

function circlePoints(radius: number, segments = 56) {
  return Array.from({ length: segments + 1 }, (_, index) => {
    const angle = (index / segments) * Math.PI * 2;
    return [Math.cos(angle) * radius, 0, Math.sin(angle) * radius] as [number, number, number];
  });
}

function OrbitingGroup({
  radius,
  initialAngle,
  speed,
  children,
}: {
  radius: number;
  initialAngle: number;
  speed: number;
  children: ReactNode;
}) {
  const orbitRef = useRef<THREE.Group>(null);

  useFrame(({ clock }) => {
    if (orbitRef.current) {
      orbitRef.current.rotation.y = initialAngle + clock.elapsedTime * speed;
    }
  });

  return (
    <group ref={orbitRef}>
      <group position={[radius, 0, 0]}>{children}</group>
    </group>
  );
}

function PoiMarker({
  poi,
  position,
  size,
  expanded,
  selected,
  onSelect,
  onContext,
}: {
  poi: SystemPointOfInterest;
  position: [number, number, number];
  size: number;
  expanded?: boolean;
  selected?: boolean;
  onSelect?: (poi: SystemPointOfInterest, event: ThreeEvent<MouseEvent>) => void;
  onContext?: (poi: SystemPointOfInterest, event: ThreeEvent<MouseEvent>) => void;
}) {
  const color = poiColors[poi.type];
  const markerRef = useRef<THREE.Group>(null);

  useFrame((_, delta) => {
    if (markerRef.current) {
      markerRef.current.rotation.y += delta * (poi.type === "asteroid_belt" ? 0.55 : 0.22);
    }
  });

  return (
    <group ref={markerRef} position={position}>
      {selected && (
        <mesh scale={size * 2.6}>
          <sphereGeometry args={[1, 16, 16]} />
          <meshBasicMaterial color="#f0a030" transparent opacity={0.18} depthWrite={false} />
        </mesh>
      )}
      <mesh
        onClick={(event) => {
          event.stopPropagation();
          onSelect?.(poi, event);
        }}
        onContextMenu={(event) => {
          event.stopPropagation();
          onContext?.(poi, event);
        }}
        onPointerOver={(event) => {
          event.stopPropagation();
          document.body.style.cursor = "pointer";
        }}
        onPointerOut={() => {
          document.body.style.cursor = "";
        }}
      >
        {poi.type === "station" ? <boxGeometry args={[size * 1.4, size * 1.4, size * 1.4]} /> : null}
        {poi.type === "asteroid_belt" ? <torusGeometry args={[size * 1.05, size * 0.22, 8, 18]} /> : null}
        {poi.type === "anomaly" ? <octahedronGeometry args={[size * 1.2, 0]} /> : null}
        {poi.type === "stargate" ? <torusGeometry args={[size * 1.25, size * 0.18, 8, 24]} /> : null}
        {poi.type === "site" ? <dodecahedronGeometry args={[size, 0]} /> : null}
        <meshBasicMaterial color={selected ? "#f0a030" : color} transparent opacity={expanded || selected ? 0.98 : 0.75} />
      </mesh>
      {(expanded || selected) && (
        <Billboard position={[0, size * 2.4, 0]}>
          <Text color={selected ? "#f0a030" : color} fontSize={size * 1.9} anchorX="center" anchorY="middle" outlineColor="#040810" outlineWidth={0.04}>
            {poi.name}
          </Text>
        </Billboard>
      )}
    </group>
  );
}

export function StarSystemContents({
  systemId,
  systemName,
  starColor,
  planets,
  pointsOfInterest,
  selectedPoiId = null,
  expanded = false,
  scale = 1,
  onPoiSelect,
  onPoiContext,
}: StarSystemContentsProps) {
  const displayPlanets = expanded ? planets : planets.slice(0, 5);
  const displayPois = expanded ? pointsOfInterest : pointsOfInterest.slice(0, 4);
  const labelScale = Math.min(scale, 1.45);
  const maxOrbit = Math.max(4.8, ...displayPlanets.map((planet) => 2 + planet.orbit * 0.42)) * scale;
  const poiRingStart = maxOrbit + (expanded ? 2.2 : 1.45) * scale;
  const poiRingGap = (expanded ? 1.25 : 0.95) * scale;
  const outerPoiRadius = displayPois.length ? poiRingStart + (displayPois.length - 1) * poiRingGap : maxOrbit;
  const gravityRadius = Math.max(maxOrbit, outerPoiRadius) + (expanded ? 1.4 : 0.9) * scale;

  return (
    <group>
      <Line points={circlePoints(gravityRadius)} color={starColor} lineWidth={1} transparent opacity={expanded ? 0.22 : 0.1} />
      {displayPlanets.map((planet, index) => {
        const orbitRadius = (2.4 + planet.orbit * 0.42) * scale;
        const seed = hashString(`${systemId}-${planet.id}`);
        const angle = ((seed % 360) / 360) * Math.PI * 2;
        const direction = seed % 5 === 0 ? -1 : 1;
        const planetSize = (planet.type.includes("Gas") ? 0.42 : 0.28) * scale * (expanded ? 1.15 : 0.85);
        const color = planetColors[planet.type] ?? "#cbd5e1";

        return (
          <group key={planet.id}>
            {(expanded || index < 3) && <Line points={circlePoints(orbitRadius)} color="#334155" lineWidth={1} transparent opacity={expanded ? 0.34 : 0.16} />}
            <OrbitingGroup radius={orbitRadius} initialAngle={angle} speed={getOrbitalSpeed(orbitRadius, direction)}>
              <mesh>
                <sphereGeometry args={[planetSize, 12, 12]} />
                <meshBasicMaterial color={color} />
              </mesh>
              {expanded && (
                <Billboard position={[0, planetSize * 3.2, 0]}>
                  <Text color={color} fontSize={0.58 * labelScale} anchorX="center" anchorY="middle" outlineColor="#040810" outlineWidth={0.04}>
                    {planet.name}
                  </Text>
                </Billboard>
              )}
            </OrbitingGroup>
          </group>
        );
      })}
      {displayPois.map((poi) => {
        const orbit = getSystemPoiOrbitSpec({ systemId, planets, pointsOfInterest, poiId: poi.id, scale, expanded });
        const markerSize = Math.min(0.34 * scale * (expanded ? 1.15 : 0.9), expanded ? 0.62 : 0.42);
        return (
          <OrbitingGroup key={poi.id} radius={orbit.radius} initialAngle={orbit.initialAngle} speed={orbit.speed}>
            <PoiMarker
              poi={poi}
              position={[0, orbit.y, 0]}
              size={markerSize}
              expanded={expanded}
              selected={poi.id === selectedPoiId}
              onSelect={onPoiSelect}
              onContext={onPoiContext}
            />
          </OrbitingGroup>
        );
      })}
      {expanded && (
        <Billboard position={[0, -2.6 * scale, 0]}>
          <Text color="#94a3b8" fontSize={0.62 * labelScale} anchorX="center" anchorY="middle" outlineColor="#040810" outlineWidth={0.04}>
            {systemName} gravity well
          </Text>
        </Billboard>
      )}
    </group>
  );
}
