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

interface CharacterProps {
    isDriving: boolean
    onEnter: () => void
    carRef: React.RefObject<Mesh | null>
}

export function Character({ isDriving, onEnter, carRef }: CharacterProps) {
    const ref = useRef<Mesh>(null)
    const { scene } = useGLTF('/character.glb')
    const { camera } = useThree()
    const keys = useRef({ w: false, a: false, s: false, d: false, shift: false, space: false })
    const velocity = useRef(new Vector3(0, 0, 0))

    // Camera state
    const cameraState = useRef({
        yaw: 0,
        pitch: -0.3, // Start looking down slightly
        distance: 10
    })

    useEffect(() => {
        const handleKeyDown = (e: KeyboardEvent) => {
            const key = e.key.toLowerCase()
            if (keys.current.hasOwnProperty(key)) {
                (keys.current as any)[key] = true
            }
            if (e.key === 'Shift') keys.current.shift = true
            if (e.key === ' ') keys.current.space = true

            if (e.key.toLowerCase() === 'f') {
                if (!isDriving && carRef.current && ref.current) {
                    const dist = ref.current.position.distanceTo(carRef.current.position)
                    if (dist < 5) {
                        onEnter()
                    }
                }
            }
        }
        const handleKeyUp = (e: KeyboardEvent) => {
            const key = e.key.toLowerCase()
            if (keys.current.hasOwnProperty(key)) {
                (keys.current as any)[key] = false
            }
            if (e.key === 'Shift') keys.current.shift = false
            if (e.key === ' ') keys.current.space = false
        }

        const handleClick = () => {
            document.body.requestPointerLock()
        }

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

                // Clamp pitch to avoid flipping
                // Min: -PI/2 (looking down from top)
                // Max: PI/2 (looking up from bottom) - allow full range, collision will handle ground
                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.body.addEventListener('click', handleClick)
        document.addEventListener('mousemove', handleMouseMove)

        return () => {
            window.removeEventListener('keydown', handleKeyDown)
            window.removeEventListener('keyup', handleKeyUp)
            document.body.removeEventListener('click', handleClick)
            document.removeEventListener('mousemove', handleMouseMove)
        }
    }, [isDriving, onEnter, carRef])

    // Sync position when exiting car
    useEffect(() => {
        if (!isDriving && carRef.current && ref.current) {
            // Place character next to car
            const exitOffset = new Vector3(3, 0, 0)
            exitOffset.applyEuler(carRef.current.rotation)
            ref.current.position.copy(carRef.current.position).add(exitOffset)

            // Sync rotation (face opposite direction of car)
            ref.current.rotation.copy(carRef.current.rotation)
            ref.current.rotation.y += Math.PI

            // Set camera yaw to match character rotation (behind character)
            cameraState.current.yaw = ref.current.rotation.y

            // Reset pitch
            cameraState.current.pitch = -0.3

            ref.current.position.y = 1 // Ensure above ground
            velocity.current.set(0, 0, 0)
        }
    }, [isDriving])

    useFrame((_state, delta) => {
        if (!ref.current || isDriving) return

        const baseSpeed = 10
        const sprintMultiplier = keys.current.shift ? 2 : 1
        const speed = baseSpeed * sprintMultiplier

        const direction = new Vector3()

        // Calculate forward and right vectors based on camera yaw
        const forward = new Vector3(0, 0, -1).applyAxisAngle(new Vector3(0, 1, 0), cameraState.current.yaw)
        const right = new Vector3(1, 0, 0).applyAxisAngle(new Vector3(0, 1, 0), cameraState.current.yaw)

        if (keys.current.w) direction.add(forward)
        if (keys.current.s) direction.sub(forward)
        if (keys.current.a) direction.sub(right)
        if (keys.current.d) direction.add(right)

        if (direction.length() > 0) {
            direction.normalize().multiplyScalar(speed * delta)
            ref.current.position.add(direction)

            // Simple lookAt for now
            ref.current.lookAt(ref.current.position.clone().add(direction))
        }

        // Physics (Gravity & Jumping)
        const gravity = 30
        const jumpForce = 12
        const groundY = 0.5

        // Apply gravity
        velocity.current.y -= gravity * delta

        // Apply velocity to position
        ref.current.position.y += velocity.current.y * delta

        // Ground collision
        if (ref.current.position.y <= groundY) {
            ref.current.position.y = groundY
            velocity.current.y = 0

            // Jump
            if (keys.current.space) {
                velocity.current.y = jumpForce
            }
        }

        // Update Camera Position (Orbit)
        const { yaw, pitch, distance } = cameraState.current

        // Target position (slightly above character)
        const targetPos = ref.current.position.clone().add(new Vector3(0, 2, 0))

        // Calculate max camera position
        const maxCamPos = new Vector3(0, 0, distance)
        maxCamPos.applyEuler(new Euler(pitch, yaw, 0, 'YXZ'))

        // Check for ground collision
        // Ground is at y=0. We want camera to be at least y=0.5
        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
            // potentialWorldPos.y = targetPos.y + maxCamPos.y
            // We want: targetPos.y + (maxCamPos.y * ratio) = minCameraHeight
            // maxCamPos.y * ratio = minCameraHeight - targetPos.y
            // ratio = (minCameraHeight - targetPos.y) / maxCamPos.y

            // Note: maxCamPos.y should be negative here if we are hitting ground
            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, 0, 'YXZ'))

        camera.position.copy(targetPos).add(actualCamPos)
        camera.lookAt(targetPos)
    })

    return (
        <mesh ref={ref} position={[0, 0.5, 0]} visible={!isDriving}>
            <primitive object={scene} scale={1.5} rotation={[0, 0, 0]} />
        </mesh>
    )
}
