import { useRef, useEffect, forwardRef, useImperativeHandle } from 'react'
import { useFrame, useThree } from '@react-three/fiber'
import { Vector3, Mesh, Euler } from 'three'
import { useGLTF } from '@react-three/drei'

interface CarProps {
    isDriving: boolean
    onExit: () => void
    position?: [number, number, number]
}

export const Car = forwardRef<Mesh, CarProps>(({ isDriving, onExit, position = [5, 0.5, 5] }, ref) => {
    const internalRef = useRef<Mesh>(null)
    useImperativeHandle(ref, () => internalRef.current!)

    const { scene } = useGLTF('/car.glb')
    const { camera } = useThree()
    const keys = useRef({ w: false, a: false, s: false, d: false, f: false })
    const speed = useRef(0)

    // Camera state for car
    const cameraState = useRef({
        yaw: 0,
        pitch: -0.2, // Adjusted to -0.2
        distance: 15
    })

    useEffect(() => {
        // Reset camera yaw and pitch when entering vehicle
        if (isDriving) {
            cameraState.current.yaw = 0
            cameraState.current.pitch = -0.2 // Adjusted to -0.2
        }
    }, [isDriving])

    useEffect(() => {
        const handleKeyDown = (e: KeyboardEvent) => {
            const key = e.key.toLowerCase()
            if (keys.current.hasOwnProperty(key)) {
                (keys.current as any)[key] = true
            }
            if (e.key.toLowerCase() === 'f') {
                if (isDriving) {
                    onExit()
                }
            }
        }
        const handleKeyUp = (e: KeyboardEvent) => {
            const key = e.key.toLowerCase()
            if (keys.current.hasOwnProperty(key)) {
                (keys.current as any)[key] = false
            }
        }

        const handleMouseMove = (e: MouseEvent) => {
            if (isDriving && document.pointerLockElement === document.body) {
                cameraState.current.yaw -= e.movementX * 0.002
                cameraState.current.pitch -= e.movementY * 0.002

                // Relaxed pitch limits
                cameraState.current.pitch = Math.max(-Math.PI / 2 + 0.1, Math.min(Math.PI / 2 - 0.1, cameraState.current.pitch))
            }
        }

        window.addEventListener('keydown', handleKeyDown)
        window.addEventListener('keyup', handleKeyUp)
        document.addEventListener('mousemove', handleMouseMove)

        return () => {
            window.removeEventListener('keydown', handleKeyDown)
            window.removeEventListener('keyup', handleKeyUp)
            document.removeEventListener('mousemove', handleMouseMove)
        }
    }, [isDriving, onExit])

    useFrame((_state, delta) => {
        if (!internalRef.current) return

        if (isDriving) {
            // Car Physics / Movement
            const maxSpeed = 60
            const acceleration = 30
            const friction = 5
            const turnSpeed = 2

            if (keys.current.w) speed.current += acceleration * delta
            if (keys.current.s) speed.current -= acceleration * delta

            // Friction
            if (!keys.current.w && !keys.current.s) {
                if (speed.current > 0) speed.current -= friction * delta
                if (speed.current < 0) speed.current += friction * delta
                if (Math.abs(speed.current) < 0.1) speed.current = 0
            }

            // Clamp speed
            speed.current = Math.max(-maxSpeed / 2, Math.min(maxSpeed, speed.current))

            // Steering
            if (Math.abs(speed.current) > 0.1) {
                if (keys.current.a) internalRef.current.rotation.y += turnSpeed * delta * (speed.current > 0 ? 1 : -1)
                if (keys.current.d) internalRef.current.rotation.y -= turnSpeed * delta * (speed.current > 0 ? 1 : -1)
            }

            // Move
            internalRef.current.translateZ(speed.current * delta)

            // Camera Follow
            const { yaw, pitch, distance } = cameraState.current

            // Target position (above car)
            const targetPos = internalRef.current.position.clone().add(new Vector3(0, 2, 0))

            // Calculate max camera position (relative to target)
            const maxCamPos = new Vector3(0, 0, distance)
            // Apply rotation: pitch, then yaw + car rotation + 180deg (to look from behind)
            maxCamPos.applyEuler(new Euler(pitch, yaw + internalRef.current.rotation.y + Math.PI, 0, 'YXZ'))

            // Check for ground collision
            const minCameraHeight = 0.5
            let actualDistance = distance

            // Calculate world position of camera if at full distance
            const potentialWorldPos = targetPos.clone().add(maxCamPos)

            if (potentialWorldPos.y < minCameraHeight) {
                // We need to shorten the distance so that worldPos.y === minCameraHeight
                if (maxCamPos.y < 0) {
                    const ratio = (minCameraHeight - targetPos.y) / maxCamPos.y
                    actualDistance = distance * ratio
                }
            }

            const actualCamPos = new Vector3(0, 0, actualDistance)
            actualCamPos.applyEuler(new Euler(pitch, yaw + internalRef.current.rotation.y + Math.PI, 0, 'YXZ'))

            // Lerp to the collided position
            // Increased lerp speed from 0.1 to 0.5 to reduce lag
            camera.position.lerp(targetPos.clone().add(actualCamPos), 0.5)
            camera.lookAt(targetPos)
        }
    })

    return (
        <mesh ref={internalRef} position={new Vector3(...position)}>
            <primitive object={scene} scale={1.5} rotation={[0, 0, 0]} />
        </mesh>
    )
})
