import { useFrame, useThree } from "@react-three/fiber";
import { useEffect, useRef } from "react";
import { FEAR_SETTINGS, fearState } from "../state";
import { PointerLockControls } from "@react-three/drei";
import * as THREE from "three";

const forward = new THREE.Vector3();
const side = new THREE.Vector3();
const viewDirection = new THREE.Vector3();
const targetDest = new THREE.Vector3();

const playerRoot = new THREE.Vector3(0, FEAR_SETTINGS.PLAYER_HEIGHT, 0);
const targetVelocity = new THREE.Vector3();
const currentVelocity = new THREE.Vector3();

function usePlayerControls() {
    const keys = useRef({ Forward: false, Backward: false, Left: false, Right: false });

    useEffect(() => {
        const handleKeyDown = (e: KeyboardEvent) => {
            if (e.code === 'KeyW' || e.code === 'ArrowUp') keys.current.Forward = true;
            if (e.code === 'KeyS' || e.code === 'ArrowDown') keys.current.Backward = true;
            if (e.code === 'KeyA' || e.code === 'ArrowLeft') keys.current.Left = true;
            if (e.code === 'KeyD' || e.code === 'ArrowRight') keys.current.Right = true;
        };

        const handleKeyUp = (e: KeyboardEvent) => {
            if (e.code === 'KeyW' || e.code === 'ArrowUp') keys.current.Forward = false;
            if (e.code === 'KeyS' || e.code === 'ArrowDown') keys.current.Backward = false;
            if (e.code === 'KeyA' || e.code === 'ArrowLeft') keys.current.Left = false;
            if (e.code === 'KeyD' || e.code === 'ArrowRight') keys.current.Right = false;
        };

        window.addEventListener('keydown', handleKeyDown);
        window.addEventListener('keyup', handleKeyUp);

        return () => {
            window.removeEventListener('keydown', handleKeyDown);
            window.removeEventListener('keyup', handleKeyUp);
        };
    }, []);

    return keys.current;
}

export default function Player() {
    const { camera } = useThree();
    const controls = usePlayerControls();

    const flashlightRef = useRef<THREE.SpotLight>(null);
    const movementCounter = useRef<number>(0);
    const bobIntensity = useRef<number>(0);

    const confirmedSegment = useRef<number>(0);
    const hasTriggeredThisSegment = useRef<boolean>(false);

    useEffect(() => {
        playerRoot.set(camera.position.x, FEAR_SETTINGS.PLAYER_HEIGHT, camera.position.z);
    }, []);

    useFrame((state, delta) => {
        const dt = Math.min(delta, 0.1);

        camera.getWorldDirection(forward);
        forward.y = 0;
        forward.normalize();
        side.crossVectors(forward, THREE.Object3D.DEFAULT_UP).normalize();

        const moveForward = Number(controls.Forward) - Number(controls.Backward);
        const moveSide = Number(controls.Right) - Number(controls.Left);

        targetVelocity.set(0, 0, 0);
        if (moveForward !== 0) targetVelocity.addScaledVector(forward, moveForward);
        if (moveSide !== 0) targetVelocity.addScaledVector(side, moveSide);

        if (targetVelocity.lengthSq() > 0) 
            targetVelocity.normalize().multiplyScalar(FEAR_SETTINGS.PLAYER_SPEED);

        currentVelocity.lerp(targetVelocity, 10 * dt);

        playerRoot.x += currentVelocity.x * dt;
        playerRoot.z += currentVelocity.z * dt;

        const minX = -fearState.currentWidth / 2 + FEAR_SETTINGS.WALL_BUFFER;
        const maxX = fearState.currentWidth / 2 - FEAR_SETTINGS.WALL_BUFFER;
        playerRoot.x = THREE.MathUtils.clamp(playerRoot.x, minX, maxX);

        const isMoving = controls.Forward || controls.Backward || controls.Left || controls.Right;
        
        bobIntensity.current = THREE.MathUtils.lerp(bobIntensity.current, isMoving ? 1 : 0, 8 * dt);
        
        if (isMoving) 
            movementCounter.current += dt * 12;

        const moveBobY = Math.sin(movementCounter.current) * 0.06 * bobIntensity.current;
        const moveBobX = Math.cos(movementCounter.current / 2) * 0.04 * bobIntensity.current;

        const breatheTime = state.clock.elapsedTime * 1.8;
        const breatheBobY = Math.sin(breatheTime) * 0.03 * (1 - bobIntensity.current * 0.5);

        camera.position.copy(playerRoot);
        camera.position.y += moveBobY + breatheBobY;
        camera.position.addScaledVector(side, moveBobX);

        if (flashlightRef.current) {
            flashlightRef.current.position.copy(camera.position);
            camera.getWorldDirection(viewDirection);

            targetDest
                .copy(camera.position)
                .addScaledVector(viewDirection, 10);

            flashlightRef.current.target.position.lerp(targetDest, 12 * dt);
            flashlightRef.current.target.updateMatrixWorld();

            flashlightRef.current.intensity = 
                FEAR_SETTINGS.FLASHLIGHT_INTENSITY_BASE + 
                Math.sin(state.clock.elapsedTime * 30) * 0.15 * Math.cos(state.clock.elapsedTime * 3);
        }

        const length = FEAR_SETTINGS.HALLWAY_LENGTH;
        const absoluteZ = -playerRoot.z; 
        const rawSegmentIndex = Math.floor(absoluteZ / length);
        const progressZ = ((absoluteZ % length) + length) % length / length;

        if (rawSegmentIndex > confirmedSegment.current && progressZ > 0.25) {
            if (!hasTriggeredThisSegment.current) {
                fearState.registerLoop('forward');
                hasTriggeredThisSegment.current = true;
            }
            confirmedSegment.current = rawSegmentIndex;
        }
        else if (rawSegmentIndex < confirmedSegment.current && progressZ < 0.75) {
            if (!hasTriggeredThisSegment.current) {
                fearState.registerLoop('backward');
                hasTriggeredThisSegment.current = true;
            }
            confirmedSegment.current = rawSegmentIndex;
        }

        if (rawSegmentIndex === confirmedSegment.current && progressZ > 0.35 && progressZ < 0.65) {
            hasTriggeredThisSegment.current = false;
        }
    });

    return (
        <>
            <PointerLockControls />
            <spotLight
                ref={flashlightRef}
                distance={25}
                angle={0.35}
                penumbra={0.8}
                intensity={0}
                color="#fffaed"
                decay={2}
                castShadow
                shadow-bias={-0.001}
            />
        </>
    );
}