import React, { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
import { useSceneStore } from '../store/useSceneStore'
import { computeTruckProgress } from '../hooks/useTruckMovement'
import { animateWheels } from '../animations/wheelAnimation'
import { easing } from 'maath'
import { truckPath } from '../curves/truckPath'

/**
 * TruckAnimation
 * ---------------------------------------------------------------------------
 * PERF: previously subscribed to `scrollProgress` (re-running useTruckMovement's
 * useMemos and re-rendering every scroll frame) and pushed `truckProgress` back
 * into the store each render. Now it subscribes to nothing — scroll progress is
 * read transiently via `getState()` inside useFrame and the mapping is a pure,
 * allocation-free function. The store sync was removed (nothing reads
 * truckProgress). Net: zero React renders while the truck drives; identical motion.
 */
export default function TruckAnimation({ truckRef, wheelRefs }) {
  const initialized = useRef(false)

  // Float trackers for 1D progress and direction detection
  const dampedProgressRef = useRef(0)
  const lastScrollProgressRef = useRef(0)
  const isReversingRef = useRef(false)

  // Tracker for smooth 180-degree yaw rotation (prevents glitches by pivoting Y rotation angle directly)
  const extraRotationRef = useRef(0)
  
  // Track wheel rotation accumulation
  const accumulatedRotationRef = useRef(0)
  const lastDampedProgressRef = useRef(0)
  useFrame((state, delta) => {

    if (!truckRef.current) return

    // r3f can emit delta === 0 (coincident frames, the first frame, or after a
    // long main-thread block while the 32MB scene parses). maath's easing.damp
    // divides by delta internally, so a 0 yields NaN/Infinity that poisons the
    // damper's stored velocity — and from then on truckPath.getPoint(NaN) throws
    // "Cannot read properties of undefined (reading 'x')". Clamp delta to a safe
    // positive range before any damping.
    const dt = Number.isFinite(delta) && delta > 0 ? Math.min(delta, 0.1) : 1 / 60

    // Read scroll progress transiently (no subscription, no re-render) and map
    // it to spline progress with the pure piecewise function.
    const scrollProgress = useSceneStore.getState().scrollProgress
    const truckProgress = computeTruckProgress(scrollProgress)

    // Detect scroll direction changes from the actual page scroll progress
    const deltaScroll = scrollProgress - lastScrollProgressRef.current
    if (deltaScroll < -0.0001) {
      isReversingRef.current = true
    } else if (deltaScroll > 0.0001) {
      isReversingRef.current = false
    }
    lastScrollProgressRef.current = scrollProgress

    // Ensure correct parent-child structure and orientation for the truck (runs reactively on re-renders)
    const innerGroup = truckRef.current.children[0]
    if (innerGroup && truckRef.current.children.length > 1) {
      const siblings = [...truckRef.current.children].slice(1)
      siblings.forEach((sibling) => {
        innerGroup.attach(sibling)
      })

      innerGroup.rotation.set(0, -Math.PI / 2, 0)

      // Disable frustum culling on all child meshes so the truck/shadow is always visible
      truckRef.current.traverse((child) => {
        if (child.isMesh) {
          child.frustumCulled = false
          child.castShadow = true
          child.receiveShadow = true
        }
      })
    }

    // Run one-time state initialization for progress trackers
    if (!initialized.current) {
      dampedProgressRef.current = truckProgress
      dampedProgressRef.current_velocity = 0
      lastDampedProgressRef.current = truckProgress
      lastScrollProgressRef.current = scrollProgress
      isReversingRef.current = false
      extraRotationRef.current = 0
      extraRotationRef.current_velocity = 0

      const position = truckPath.getPoint(dampedProgressRef.current)
      let lookAtTargetVector
      if (dampedProgressRef.current >= 0.99) {
        const tangent = truckPath.getTangent(1.0)
        const endPoint = truckPath.getPoint(1.0)
        lookAtTargetVector = new THREE.Vector3().copy(endPoint).addScaledVector(tangent, 1.0)
      } else {
        const ahead = Math.min(dampedProgressRef.current + 0.01, 1.0)
        lookAtTargetVector = truckPath.getPoint(ahead)
      }

      truckRef.current.position.copy(position)
      if (truckRef.current.position.distanceToSquared(lookAtTargetVector) > 0.0001) {
        truckRef.current.lookAt(lookAtTargetVector)
      }

      initialized.current = true
    }

    // Smoothly damp the 1D progress scalar along the curve path
    easing.damp(dampedProgressRef, 'current', truckProgress, 0.30, dt)

    // Defensive: keep the spline parameter a finite value in [0,1]. getPoint(NaN)
    // or an out-of-range t reads an undefined curve point and throws.
    if (!Number.isFinite(dampedProgressRef.current)) {
      dampedProgressRef.current = truckProgress
      if (dampedProgressRef.__damp) dampedProgressRef.__damp = {} // clear any poisoned velocity
    }
    dampedProgressRef.current = THREE.MathUtils.clamp(dampedProgressRef.current, 0, 1)

    // Evaluate the 3D position and orientation directly on the spline curve
    const position = truckPath.getPoint(dampedProgressRef.current)
    
    let lookAtTargetVector
    if (dampedProgressRef.current >= 0.99) {
      const tangent = truckPath.getTangent(1.0)
      const endPoint = truckPath.getPoint(1.0)
      lookAtTargetVector = new THREE.Vector3().copy(endPoint).addScaledVector(tangent, 1.0)
    } else {
      const ahead = Math.min(dampedProgressRef.current + 0.01, 1.0)
      lookAtTargetVector = truckPath.getPoint(ahead)
    }

    // Update position and base forward rotation directly (ensures 100% spline compliance, zero corner cutting)
    truckRef.current.position.copy(position)
    if (truckRef.current.position.distanceToSquared(lookAtTargetVector) > 0.0001) {
      truckRef.current.lookAt(lookAtTargetVector)
    }

    // Determine target extra rotation:
    // - 0 radians when moving forward
    // - Math.PI radians (180 degrees) when reversing
    // We disable U-turns at the extreme start and end of the path to keep the truck stable at warehouse/delivery spots
    let targetExtraRotation = 0
    if (dampedProgressRef.current > 0.05 && dampedProgressRef.current < 0.95) {
      if (isReversingRef.current) {
        targetExtraRotation = Math.PI
      }
    }

    // Smoothly damp the extra rotation angle directly (prevents pitch/roll glitches or 3D target collapse)
    easing.damp(extraRotationRef, 'current', targetExtraRotation, 0.20, dt)

    // Defensive: reset extra rotation if it becomes NaN
    if (!Number.isFinite(extraRotationRef.current)) {
      extraRotationRef.current = targetExtraRotation
      extraRotationRef.current_velocity = 0
      if (extraRotationRef.__damp) extraRotationRef.__damp = {}
    }

    // Apply the yaw pivot around the local vertical axis
    truckRef.current.rotateY(extraRotationRef.current)

    // Calculate progress delta for wheels and audio
    const deltaDamped = Math.abs(dampedProgressRef.current - lastDampedProgressRef.current)
    lastDampedProgressRef.current = dampedProgressRef.current

    // Accumulate wheel rotation based on absolute movement delta so they always roll forward locally
    const isMoving = dampedProgressRef.current > 0.001 && dampedProgressRef.current < 0.999
    if (isMoving) {
      accumulatedRotationRef.current += deltaDamped * 250 // spinFactor
    }

    // Spin wheels
    animateWheels(wheelRefs, accumulatedRotationRef.current)



    // Add engine vibration to the inner group to prevent coordinate pollution on the root group
    if (truckRef.current.children && truckRef.current.children[0]) {
      const innerGroup = truckRef.current.children[0]
      innerGroup.position.y = Math.sin(state.clock.getElapsedTime() * 45) * 0.003
    }
  })

  return null
}
