import { useTexture, PositionalAudio } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import { useEffect, useMemo, useRef, useState } from "react";

import * as THREE from "three";
import { FEAR_SETTINGS, fearState } from "../state";
import { ShaderPatch } from "../shader-patch";

useTexture.preload('fear/img/creature.png');

export default function TheCreature() {
    const baseTexture = useTexture('fear/img/creature.png');

    const texture = useMemo(() => {
        const t = baseTexture.clone();
        t.needsUpdate = true;
        return t;
    }, [baseTexture]);

    const meshRef = useRef<THREE.Mesh>(null);
    const audioRef = useRef<THREE.PositionalAudio>(null);
    const { camera } = useThree();

    const [hasTriggered, setHasTriggered] = useState(false);
    const [isSpawned, setIsSpawned] = useState(false);

    const globalDistance = useRef<number>(32);
    const [finaleTriggered, setFinaleTriggered] = useState(fearState.finaleTriggered);

    const audioPlaying = useRef<boolean>(false);

    const movePhase = useRef<'frozen' | 'lurching'>('frozen');
    const phaseTimer = useRef<number>(1.5);

    const glitchCooldown = useRef<number>(0);
    const isGlitchSpiking = useRef<boolean>(false);
    const flickerCooldown = useRef<number>(0);

    useEffect(() => {
        const unsubscribe = fearState.subscribe(() => {
            setFinaleTriggered(fearState.finaleTriggered);

            if (!fearState.finaleTriggered) {
                setIsSpawned(false);
                setHasTriggered(false);
                globalDistance.current = 32;
                audioPlaying.current = false;
                movePhase.current = 'frozen';
                phaseTimer.current = 1.5;

                if (audioRef.current && audioRef.current.isPlaying)
                    audioRef.current.stop();
            }
        });
        return () => unsubscribe();
    }, []);

    useFrame((state, delta) => {
        if (!fearState.finaleTriggered) return;

        const creature = meshRef.current;
        if (!creature) return;

        if (!isSpawned) {
            setIsSpawned(true);
            globalDistance.current = 32;
            movePhase.current = 'frozen';
            phaseTimer.current = 1.0 + Math.random() * 1.5;
        }

        if (!hasTriggered) {
            if (globalDistance.current < 40)
                setHasTriggered(true);

        }

        if (hasTriggered) {
            phaseTimer.current -= delta;

            if (phaseTimer.current <= 0) {
                if (movePhase.current === 'frozen') {
                    movePhase.current = 'lurching';
                    phaseTimer.current = 0.05 + Math.random() * 0.2;
                } else {
                    movePhase.current = 'frozen';
                    const proximityFactor = Math.max(0.05, globalDistance.current / 32);
                    phaseTimer.current = (0.2 + Math.random() * 1.0) * proximityFactor;
                }
            }

            if (movePhase.current === 'lurching') {
                globalDistance.current -= FEAR_SETTINGS.CREATURE_SPEED * 3 * delta;
            }

            if (audioRef.current && !audioPlaying.current) {
                audioPlaying.current = true;
                if (audioRef.current.context.state === 'suspended')
                    audioRef.current.context.resume();
                audioRef.current.play();
            }

            const shakeIntensity = Math.max(0, 1 - (globalDistance.current / 32)) * 0.22;
            camera.position.x += (Math.random() - 0.5) * shakeIntensity;
            camera.position.y += (Math.random() - 0.5) * shakeIntensity;

            if (globalDistance.current <= 0.1) {
                window.location.href = '/';
                fearState.registerCaught();
                return;
            }
        }

        const forwardVector = new THREE.Vector3();
        camera.getWorldDirection(forwardVector);
        const lookDirZ = forwardVector.z < 0 ? -1 : 1;

        const calculatedZ = camera.position.z + (lookDirZ * globalDistance.current);

        creature.position.set(0, 1.6, calculatedZ);
        creature.lookAt(camera.position.x, creature.position.y, camera.position.z);

        if (!hasTriggered) return;

        const proximity = 1 - Math.max(0, Math.min(1, globalDistance.current / 32));
        const jitterX = 1.0 + (Math.random() - 0.5) * 0.04 * proximity;
        let jitterY = 1.0 + (Math.random() - 0.5) * 0.06 * proximity;

        glitchCooldown.current -= delta;
        if (glitchCooldown.current <= 0) {
            if (Math.random() < 0.25 + proximity * 0.35) {
                isGlitchSpiking.current = true;
                glitchCooldown.current = 0.03 + Math.random() * 0.08;
            } else {
                isGlitchSpiking.current = false;
                glitchCooldown.current = 0.08 + Math.random() * 0.4 * (1 - proximity * 0.7);
            }
        }

        if (isGlitchSpiking.current) {
            const spike = 0.15 + Math.random() * 0.35;
            jitterY += Math.random() > 0.5 ? spike : -spike * 0.6;
        }

        creature.scale.set(jitterX, jitterY, 1.0);

        flickerCooldown.current -= delta;
        if (flickerCooldown.current <= 0) {
            if (creature.visible && Math.random() < 0.12 + proximity * 0.08) {
                creature.visible = false;
                flickerCooldown.current = 0.02 + Math.random() * 0.05;
            } else {
                creature.visible = true;
                flickerCooldown.current = 0.05 + Math.random() * 0.3 * (1 - proximity * 0.5);
            }
        }

        texture.offset.set(
            (Math.random() - 0.5) * 0.025 * proximity,
            (Math.random() - 0.5) * 0.025 * proximity
        );

        if (proximity > 0.2) {
            creature.position.x += (Math.random() - 0.5) * 0.12 * proximity;
            creature.position.y += (Math.random() - 0.5) * 0.06 * proximity;
        }
    });

    return (
        <mesh
            ref={meshRef}
            visible={finaleTriggered}
        >
            <planeGeometry args={[3.0, 4.8]} />
            <meshStandardMaterial
                map={texture}
                transparent={true}
                depthWrite={false}
                side={THREE.DoubleSide}
                onBeforeCompile={ShaderPatch}
                emissive="#ffffff"
                emissiveMap={texture}
                emissiveIntensity={0.15}
            />

            {finaleTriggered && (
                <PositionalAudio
                    url="fear/snd/riser.mp3"
                    ref={audioRef}
                    distance={25}
                    loop={false}
                    autoplay={false}
                />
            )}
        </mesh>
    );
}