import { useRef, useMemo, useEffect } from 'react'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
import type { TransformedGraph } from './types'
import { ANIMATION } from './constants'

interface NeuralPulseProps {
  graph: TransformedGraph
  positions: Float32Array
}

export default function NeuralPulse({ graph, positions }: NeuralPulseProps) {
  const pointsRef = useRef<THREE.Points>(null!)
  const count = Math.min(ANIMATION.pulseCount, graph.edges.length)

  const state = useMemo(() => ({
    progress: Float32Array.from({ length: count }, () => Math.random()),
    edgeIndices: Array.from({ length: count }, (_, i) => i % graph.edges.length),
    posArray: new Float32Array(count * 3),
    sizeArray: new Float32Array(count).fill(0.3),
  }), [count, graph.edges.length])

  // Set up geometry
  useEffect(() => {
    const pts = pointsRef.current
    if (!pts) return
    const geo = new THREE.BufferGeometry()
    geo.setAttribute('position', new THREE.BufferAttribute(state.posArray, 3))
    pts.geometry = geo
  }, [state.posArray])

  useFrame((_, delta) => {
    if (!positions || !pointsRef.current?.geometry) return

    for (let i = 0; i < count; i++) {
      // Advance pulse along edge
      state.progress[i] += delta * ANIMATION.pulseSpeed * (0.8 + Math.random() * 0.4)
      if (state.progress[i] >= 1) {
        state.progress[i] = 0
        state.edgeIndices[i] = Math.floor(Math.random() * graph.edges.length)
      }

      const edge = graph.edges[state.edgeIndices[i]]
      const t = state.progress[i]

      // Interpolate position along edge
      const si = edge.sourceIndex, ti = edge.targetIndex
      state.posArray[i * 3]     = positions[si * 3]     + (positions[ti * 3]     - positions[si * 3])     * t
      state.posArray[i * 3 + 1] = positions[si * 3 + 1] + (positions[ti * 3 + 1] - positions[si * 3 + 1]) * t
      state.posArray[i * 3 + 2] = positions[si * 3 + 2] + (positions[ti * 3 + 2] - positions[si * 3 + 2]) * t
    }

    if (pointsRef.current.geometry.attributes.position) {
      pointsRef.current.geometry.attributes.position.needsUpdate = true
    }
  })

  return (
    <points ref={pointsRef} frustumCulled={false}>
      <pointsMaterial
        color="#7dd3fc"
        size={0.8}
        transparent
        opacity={1.0}
        depthWrite={false}
        sizeAttenuation
      />
    </points>
  )
}
