import { useLayoutEffect, useMemo, useRef } from 'react'
import { useFrame, useLoader, useThree } from '@react-three/fiber'
import * as THREE from 'three'
import type { MutableRefObject } from 'react'
import type { RavenFx } from './ravenFx'

const COUNT = 18
const APEX_Z = -2.8
const CONE_SLOPE = 0.42
const AXIS_SPEED = 5.2
const CAM_Z = 5.2

type Bird = {
  active: boolean
  born: boolean
  depth: number
  theta: number
  phase: number
  flapSpeed: number
  size: number
  flip: number
  delay: number
  speed: number
  slope: number
}

function createBirds(): Bird[] {
  const birds: Bird[] = []
  for (let i = 0; i < COUNT; i++) {
    const wave = Math.floor(i / 3)
    const theta = (i / COUNT) * Math.PI * 2 + (Math.random() - 0.5) * 0.25
    birds.push({
      active: false,
      born: false,
      depth: 0,
      theta,
      phase: Math.random() * Math.PI * 2,
      flapSpeed: 10 + Math.random() * 6,
      size: 0.2 + Math.random() * 0.28,
      flip: i % 2 === 0 ? -1 : 1,
      delay: 0.03 * wave + Math.random() * 0.04,
      speed: AXIS_SPEED * (0.85 + Math.random() * 0.35),
      slope: CONE_SLOPE * (0.9 + Math.random() * 0.25),
    })
  }
  return birds
}

/** Viewport CSS px → world on the apex plane. Uses the raven canvas rect for correct NDC. */
function screenToApexWorld(
  sx: number,
  sy: number,
  camera: THREE.PerspectiveCamera,
  glDom: HTMLCanvasElement | null,
  out: THREE.Vector3,
) {
  const canvasRect = glDom?.getBoundingClientRect()
  const left = canvasRect?.left ?? 0
  const top = canvasRect?.top ?? 0
  const viewW = canvasRect?.width || window.innerWidth
  const viewH = canvasRect?.height || window.innerHeight

  const ndcX = ((sx - left) / viewW) * 2 - 1
  const ndcY = -((sy - top) / viewH) * 2 + 1

  const dist = Math.abs(CAM_Z - APEX_Z)
  const vFov = THREE.MathUtils.degToRad(camera.fov)
  const halfH = Math.tan(vFov / 2) * dist
  const halfW = halfH * (viewW / viewH)
  out.set(ndcX * halfW, ndcY * halfH, APEX_Z)
}

type RavenFlockProps = {
  fx: MutableRefObject<RavenFx>
}

export function RavenFlock({ fx }: RavenFlockProps) {
  const meshRef = useRef<THREE.InstancedMesh>(null)
  const birds = useMemo(() => createBirds(), [])
  const dummy = useMemo(() => new THREE.Object3D(), [])
  const apex = useMemo(() => new THREE.Vector3(0, 0, APEX_Z), [])
  const texture = useLoader(THREE.TextureLoader, '/raven-silhouette.svg')
  const gl = useThree((s) => s.gl)

  useLayoutEffect(() => {
    texture.colorSpace = THREE.SRGBColorSpace
    texture.anisotropy = 4
    texture.needsUpdate = true
  }, [texture])

  useFrame((state, delta) => {
    const mesh = meshRef.current
    if (!mesh) return

    const cam = state.camera as THREE.PerspectiveCamera
    // Fixed camera — do NOT recenter on apex (that pinned birds to screen center)
    cam.position.set(0, 0, CAM_Z)
    cam.lookAt(0, 0, 0)
    cam.updateMatrixWorld()

    const { burst, opacity, apexSx, apexSy } = fx.current
    if (Number.isFinite(apexSx) && Number.isFinite(apexSy)) {
      screenToApexWorld(apexSx, apexSy, cam, gl.domElement, apex)
    }

    const mat = mesh.material as THREE.MeshBasicMaterial
    mat.opacity = opacity

    const dt = Math.min(delta, 0.05)
    const t = state.clock.elapsedTime

    for (let i = 0; i < COUNT; i++) {
      const bird = birds[i]

      if (!bird.born && burst > bird.delay) {
        bird.born = true
        bird.active = true
        bird.depth = 0.02 + Math.random() * 0.06
      }

      if (!bird.active) {
        dummy.position.set(0, -40, 0)
        dummy.scale.set(0, 0, 0)
        dummy.updateMatrix()
        mesh.setMatrixAt(i, dummy.matrix)
        continue
      }

      bird.depth += bird.speed * dt

      // Cone tip at portal; base // screen (XY)
      const radius = bird.depth * bird.slope
      const x = apex.x + Math.cos(bird.theta) * radius
      const y = apex.y + Math.sin(bird.theta) * radius
      const z = apex.z + bird.depth

      const flap = 1 + Math.sin(t * bird.flapSpeed + bird.phase) * 0.18
      const grow = THREE.MathUtils.smoothstep(bird.depth, 0.15, 3.2)
      const s = bird.size * flap * (0.15 + grow * 1.55)

      dummy.position.set(x, y, z)
      dummy.quaternion.copy(cam.quaternion)
      dummy.rotateY(bird.flip < 0 ? Math.PI : 0)
      dummy.rotateZ(Math.sin(t * bird.flapSpeed + bird.phase) * 0.16)
      dummy.scale.set(s * 1.45, s, 1)
      dummy.updateMatrix()
      mesh.setMatrixAt(i, dummy.matrix)

      if (bird.depth > 7.5) {
        bird.active = false
      }
    }

    mesh.instanceMatrix.needsUpdate = true
  })

  return (
    <instancedMesh ref={meshRef} args={[undefined, undefined, COUNT]} frustumCulled={false}>
      <planeGeometry args={[1, 0.55]} />
      <meshBasicMaterial
        map={texture}
        color="#0a0b08"
        transparent
        depthWrite={false}
        side={THREE.DoubleSide}
        toneMapped={false}
      />
    </instancedMesh>
  )
}
