import React, { useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';

// --- Galaxy Effect (skill) ---

const GALAXY_COUNT = 600;
const ARM_COUNT = 4;
const MAX_RADIUS = 0.6;

const GALAXY_PRESETS = {
  sun: {
    coreColor: [1.0, 0.8, 0.3],
    armColor: [1.0, 0.5, 0.1],
    tipColor: [1.0, 1.0, 0.7],
    lightColor: '#ffaa22',
    lightIntensity: 1.5,
    rotateSpeed: 0.8,
    spread: 1.0,
  },
};

function buildGalaxy(preset) {
  const positions = new Float32Array(GALAXY_COUNT * 3);
  const colors = new Float32Array(GALAXY_COUNT * 3);
  const randoms = new Float32Array(GALAXY_COUNT);

  for (let i = 0; i < GALAXY_COUNT; i++) {
    const i3 = i * 3;
    const arm = i % ARM_COUNT;
    const armAngle = (arm / ARM_COUNT) * Math.PI * 2;
    const t = Math.random();
    const r = t * MAX_RADIUS;
    const twist = t * Math.PI * 2.5;
    const angle = armAngle + twist;
    const scatter = (1 - t * 0.5) * 0.08 * preset.spread;

    positions[i3] = Math.cos(angle) * r + (Math.random() - 0.5) * scatter;
    positions[i3 + 1] = (Math.random() - 0.5) * 0.04 * (1 - t * 0.5);
    positions[i3 + 2] = Math.sin(angle) * r + (Math.random() - 0.5) * scatter;

    const core = preset.coreColor;
    const arm_ = preset.armColor;
    const tip = preset.tipColor;
    const c = t < 0.3
      ? core.map((v, j) => v + (arm_[j] - v) * (t / 0.3))
      : arm_.map((v, j) => v + (tip[j] - v) * ((t - 0.3) / 0.7));
    colors[i3] = c[0];
    colors[i3 + 1] = c[1];
    colors[i3 + 2] = c[2];
    randoms[i] = Math.random();
  }
  return { positions, colors, randoms };
}

function GalaxyEffect({ position = [0, 1.0, 0], scale = 1 }) {
  const preset = GALAXY_PRESETS.sun;
  const groupRef = useRef();
  const pointsRef = useRef();
  const { positions, colors, randoms } = useMemo(() => buildGalaxy(preset), []);
  const basePositions = useRef(positions);

  useFrame(({ clock }) => {
    if (!groupRef.current || !pointsRef.current) return;
    const t = clock.getElapsedTime();
    groupRef.current.rotation.y = t * preset.rotateSpeed;
    groupRef.current.rotation.x = Math.sin(t * 0.3) * 0.15;
    const pulse = 1 + Math.sin(t * 2.0) * 0.1;
    groupRef.current.scale.setScalar(scale * pulse);

    const pos = pointsRef.current.geometry.attributes.position.array;
    const base = basePositions.current;
    for (let i = 0; i < GALAXY_COUNT; i++) {
      const i3 = i * 3;
      const drift = Math.sin(t * 1.5 + randoms[i] * 10) * 0.02;
      pos[i3] = base[i3] + drift;
      pos[i3 + 1] = base[i3 + 1] + Math.sin(t * 2 + randoms[i] * 6) * 0.01;
      pos[i3 + 2] = base[i3 + 2] + drift * 0.7;
    }
    pointsRef.current.geometry.attributes.position.needsUpdate = true;
  });

  return (
    <group position={position} ref={groupRef}>
      <points ref={pointsRef}>
        <bufferGeometry>
          <bufferAttribute attach="attributes-position" array={positions.slice()} count={GALAXY_COUNT} itemSize={3} />
          <bufferAttribute attach="attributes-color" array={colors} count={GALAXY_COUNT} itemSize={3} />
        </bufferGeometry>
        <pointsMaterial transparent depthWrite={false} vertexColors opacity={0.6} size={0.012} blending={THREE.AdditiveBlending} />
      </points>
      <pointLight intensity={preset.lightIntensity} color={preset.lightColor} distance={3} />
    </group>
  );
}

// --- Sphere Effect (teleport) ---

const SPHERE_PRESET = {
  layers: [
    { radius: 0.2, segments: 48, size: 0.01, opacity: 0.4 },
    { radius: 0.26, segments: 32, size: 0.006, opacity: 0.15 },
  ],
  rotateSpeed: 0.8,
  pulseSpeed: 1.0,
  pulseRange: 0.12,
  colorFn: (normDist, layerIdx) => {
    if (layerIdx === 0) return [1.0, 0.8 + normDist * 0.15, 0.2];
    return [1.0, 0.7, 0.1];
  },
};

function buildSphereLayer(radius, segments, colorFn, layerIdx) {
  const geo = new THREE.SphereGeometry(radius, segments, segments);
  const posArr = new Float32Array(geo.attributes.position.array);
  const colArr = new Float32Array(posArr.length);
  for (let i = 0; i < posArr.length; i += 3) {
    const x = posArr[i], y = posArr[i + 1], z = posArr[i + 2];
    const dist = Math.sqrt(x * x + y * y + z * z);
    const normDist = dist / (radius || 1);
    const [r, g, b] = colorFn(normDist, layerIdx);
    colArr[i] = r;
    colArr[i + 1] = g;
    colArr[i + 2] = b;
  }
  geo.dispose();
  return { positions: posArr, colors: colArr };
}

function SphereEffect({ position = [0, 1.0, 0], scale = 1 }) {
  const groupRef = useRef();
  const layerRefs = useRef([]);
  const preset = SPHERE_PRESET;

  const layerData = useMemo(() =>
    preset.layers.map((l, i) => buildSphereLayer(l.radius, l.segments, preset.colorFn, i)),
  []);

  useFrame(({ clock }) => {
    if (!groupRef.current) return;
    const t = clock.getElapsedTime();
    const pulse = 1 + Math.sin(t * preset.pulseSpeed) * preset.pulseRange;
    groupRef.current.scale.setScalar(scale * pulse);

    layerRefs.current.forEach((ref, i) => {
      if (!ref) return;
      const speed = preset.rotateSpeed * (1 + i * 0.4);
      const dir = i % 2 === 0 ? 1 : -1;
      ref.rotation.y = t * speed * dir;
      ref.rotation.x = Math.sin(t * speed * 0.3 + i) * 0.4;
      ref.rotation.z = Math.cos(t * speed * 0.2 + i * 2) * 0.3;
    });
  });

  return (
    <group position={position} ref={groupRef}>
      {preset.layers.map((layer, i) => (
        <group key={i} ref={el => layerRefs.current[i] = el}>
          <points>
            <bufferGeometry>
              <bufferAttribute attach="attributes-position" array={layerData[i].positions} count={layerData[i].positions.length / 3} itemSize={3} />
              <bufferAttribute attach="attributes-color" array={layerData[i].colors} count={layerData[i].colors.length / 3} itemSize={3} />
            </bufferGeometry>
            <pointsMaterial transparent depthWrite={false} vertexColors opacity={layer.opacity} size={layer.size} blending={THREE.AdditiveBlending} />
          </points>
        </group>
      ))}
    </group>
  );
}

// --- BlackHole Effect (burst) - fullscreen raymarching ---

const bhVertexShader = `
attribute vec3 position;
varying vec2 vUv;
void main() {
  vUv = position.xy;
  gl_Position = vec4(position.xy, 0.0, 1.0);
}
`;

const bhFragmentShader = `
precision highp float;
varying vec2 vUv;
uniform vec2 uResolution;
uniform float uTime;
uniform float uPhase; // 0-1: blackhole, 1-2: explode, 2-3: dissipate

#define MAX_STEPS 48
#define STEP_SIZE 0.1
#define PI 3.14159265

float hash(float n){ return fract(sin(n)*43758.5453123); }
float hash2(vec2 p){ return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453); }

float noise(vec3 p){
  vec3 i=floor(p); vec3 f=fract(p); f=f*f*(3.0-2.0*f);
  float n=i.x+i.y*57.0+113.0*i.z;
  return mix(mix(mix(hash(n),hash(n+1.0),f.x),mix(hash(n+57.0),hash(n+58.0),f.x),f.y),
             mix(mix(hash(n+113.0),hash(n+114.0),f.x),mix(hash(n+170.0),hash(n+171.0),f.x),f.y),f.z);
}

vec3 starField(vec3 dir){
  vec3 col=vec3(0.0);
  col+=smoothstep(0.4,0.9,noise(dir*3.0+vec3(0.5,1.0,0.2))+0.5*noise(dir*7.0))*vec3(0.05,0.06,0.15);
  for(int i=0;i<3;i++){
    float sc=pow(8.0,float(i+1));
    vec3 g=fract(dir*sc)-0.5;
    float id=floor(dir.x*sc)*1.3+floor(dir.y*sc)*4.7+floor(dir.z*sc)*9.1;
    col+=step(0.995-float(i)*0.003,hash(id))*smoothstep(0.12,0.0,length(g))*vec3(0.9,0.95,1.0)*(1.0-float(i)*0.3);
  }
  return col;
}

// --- Phase 1: Black Hole ---
vec3 traceBlackHole(vec3 ro, vec3 rd, float mass){
  float rs=1.5*mass, rs2=rs*rs;
  vec3 pos=ro, vel=normalize(rd);
  for(int i=0;i<MAX_STEPS;i++){
    float r=length(pos);
    if(r<rs*0.98) return vec3(0.0);
    vel+=-(1.5*rs2/(r*r*r))*(pos/r)*STEP_SIZE;
    vel=normalize(vel);
    pos+=vel*STEP_SIZE;
    if(r>20.0) break;
  }
  return starField(normalize(vel));
}

// --- Phase 2: Supernova Explosion ---
vec3 supernova(vec2 uv, float t, float sr){
  vec3 col=vec3(0.0);
  float angle=atan(uv.y,uv.x);

  // sharp shockwave rings
  for(int i=0;i<3;i++){
    float delay=float(i)*0.15;
    float rt=max(0.0, t-delay);
    float ringR=rt*(2.0+float(i)*0.6);
    // very thin ring with sharp falloff
    float ringDist=abs(sr-ringR);
    float ringW=0.008+float(i)*0.004;
    float ring=exp(-ringDist*ringDist/(ringW*ringW));
    // bright edge glow just outside ring
    float edgeGlow=exp(-ringDist*ringDist/(ringW*ringW*8.0))*0.3;
    vec3 ringCol=mix(vec3(1.0,0.95,0.85),vec3(1.0,0.75,0.3),float(i)/3.0);
    float fade=exp(-rt*1.5)*(3.0-float(i)*0.5);
    col+=ringCol*(ring+edgeGlow)*fade;
  }

  // intense central flash with slow decay
  float flash=1.0/(sr*6.0+0.03)*max(0.0,1.0-t*1.2);
  col+=vec3(1.0,0.97,0.92)*flash*0.6;

  // hot debris particles with trails
  for(int i=0;i<50;i++){
    float fi=float(i);
    float a=hash(fi*1.17)*PI*2.0;
    float speed=0.8+hash(fi*2.31)*2.5;
    float r=t*speed;
    float life=1.0-smoothstep(0.0,0.6+hash(fi*3.7)*0.6, t);
    vec2 dir=vec2(cos(a),sin(a));
    vec2 ppos=dir*r;
    // spiral motion
    float spin=hash(fi*7.3)*2.0-1.0;
    ppos+=vec2(-dir.y,dir.x)*sin(t*3.0+fi)*0.1*spin;
    float d=length(uv-ppos);
    // sharp tiny particle with subtle trail
    float trail=exp(-d*d*8000.0)+exp(-pow(dot(uv-ppos,dir),2.0)*20000.0-pow(length(uv-ppos-dir*0.01),2.0)*5000.0)*0.3;
    float bright=life*trail;
    // temperature gradient: white-hot core -> gold -> amber
    float temp=hash(fi*9.1);
    vec3 pcol=mix(vec3(1.0,0.95,0.85),mix(vec3(1.0,0.75,0.3),vec3(0.9,0.5,0.1),temp),t*0.8);
    col+=pcol*bright*1.2;
  }


  return col;
}

// --- Phase 3: Supernova afterglow (reuse explosion, fade out) ---
vec3 stardust(vec2 uv, float t, float sr){
  // Continue supernova at t=1.0 state, then fade everything out
  float fade=pow(1.0-t, 2.0);
  // Run supernova frozen near end, slowly expanding
  vec2 expandedUv=uv/(1.0+t*0.3);
  float expandedSr=length(expandedUv);
  vec3 col=supernova(expandedUv, 0.85+t*0.15, expandedSr)*fade;
  return col;
}

void main(){
  float aspect=uResolution.x/uResolution.y;
  vec2 uv=vec2(vUv.x*aspect, vUv.y);
  float sr=length(uv);

  vec3 col=vec3(0.0);
  float alpha=0.0;

  if(uPhase<1.0){
    // Phase 1: Black hole forming (0 -> 1)
    float grow=uPhase*uPhase;
    vec2 bhUv=uv*16.0;
    float mass=3.0*grow;
    vec3 cp=vec3(0.0,0.0,3.0);
    vec3 rd=normalize(vec3(bhUv*(1.4-mass*0.1),-1.0));
    col=traceBlackHole(cp,rd,mass);

    float bhSr=length(bhUv);
    float rs=1.5*mass;
    float g=max(grow,0.001);
    col+=vec3(0.3,0.5,1.0)*0.5*0.01*g/(bhSr*5.0/g+0.008)*rs;
    col+=vec3(0.6,0.75,1.0)*exp(-abs(bhSr-0.055*g)*50.0/g)*g*0.6;

    col=pow(col/(1.0+col),vec3(0.45));
    alpha=clamp(grow*2.0,0.0,1.0)*clamp(length(col)*3.0,0.0,1.0);

  } else if(uPhase<1.1){
    // White flash transition
    float t=(uPhase-1.0)/0.1;
    float flash=1.0-t;
    col=vec3(1.0,0.98,0.95)*flash*flash;
    alpha=flash;

  } else if(uPhase<2.0){
    // Phase 2: Supernova explosion
    float t=(uPhase-1.1)/0.9;
    col=supernova(uv, t, sr);
    col=min(col, vec3(1.0));
    alpha=clamp(length(col)*5.0,0.0,1.0);

  } else {
    // Phase 3: Stardust fade out
    float t=(uPhase-2.0)/1.0;
    col=stardust(uv, t, sr);
    col=min(col, vec3(1.0));
    alpha=clamp(length(col)*5.0,0.0,1.0)*(1.0-t*t);
  }

  gl_FragColor=vec4(col,alpha);
}
`;

const BURST_TOTAL_DURATION = 4.5; // seconds for full animation

function BlackHoleEffect() {
  const matRef = useRef();
  const startRef = useRef(null);
  const uniforms = useMemo(() => ({
    uTime: { value: 0 },
    uPhase: { value: 0 },
    uResolution: { value: new THREE.Vector2(1, 1) },
  }), []);

  useFrame(({ clock, gl }) => {
    if (!matRef.current) return;
    const t = clock.getElapsedTime();
    if (startRef.current === null) startRef.current = t;
    const elapsed = t - startRef.current;
    // Map elapsed time to 0-3 phase range over BURST_TOTAL_DURATION
    const phase = Math.min(3.0, (elapsed / BURST_TOTAL_DURATION) * 3.0);
    matRef.current.uniforms.uTime.value = t;
    matRef.current.uniforms.uPhase.value = phase;
    const size = gl.getSize(new THREE.Vector2());
    matRef.current.uniforms.uResolution.value.copy(size);
  });

  return (
    <mesh renderOrder={999} frustumCulled={false}>
      <planeGeometry args={[2, 2]} />
      <rawShaderMaterial
        ref={matRef}
        vertexShader={bhVertexShader}
        fragmentShader={bhFragmentShader}
        uniforms={uniforms}
        transparent
        depthTest={false}
        depthWrite={false}
      />
    </mesh>
  );
}

// --- Unified export ---

function EnergySphere({ type = 'sun', position = [0, 1.0, 0], scale = 1 }) {
  if (type === 'earth' || type === 'moon') {
    return <SphereEffect position={position} scale={scale} />;
  }
  if (type === 'burst') {
    return <BlackHoleEffect />;
  }
  return <GalaxyEffect position={position} scale={scale} />;
}

export { EnergySphere };
export default EnergySphere;
