import { useCallback, useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { useThree } from '@react-three/fiber';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../../store/builder/store';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../../store/usePlayButtonStore';
import { useTriggerHandler } from '../../../../triggers/triggerHandler/useTriggerHandler';
import { useSceneContext } from '../../../../../scene/sceneContext';
import { useProductContext } from '../../../../products/productContext';

import ManufacturerAnimator from '../../animator/manufacturerAnimator';

function ManufacturerInstance({ human }: { human: HumanStatus }) {
    const { navMesh } = useNavMesh();
    const { isPlaying } = usePlayButtonStore();
    const { scene } = useThree();
    const { assetStore, materialStore, humanStore, productStore } = useSceneContext();
    const { setMaterial } = materialStore();
    const { triggerPointActions } = useTriggerHandler();
    const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
    const { getActionByUuid } = productStore();
    const { selectedProductStore } = useProductContext();
    const { selectedProduct } = selectedProductStore();
    const { setHumanActive, setHumanState, decrementHumanLoad, removeLastMaterial, setCurrentPhase } = humanStore();

    const [path, setPath] = useState<[number, number, number][]>([]);
    const isPausedRef = useRef<boolean>(false);
    const isSpeedRef = useRef<number>(0);
    const { speed } = useAnimationPlaySpeed();
    const { isPaused } = usePauseButtonStore();
    const humanAsset = getAssetById(human.modelUuid);
    const processStartTimeRef = useRef<number | null>(null);
    const processTimeRef = useRef<number>(0);
    const processAnimationIdRef = useRef<number | null>(null);
    const accumulatedPausedTimeRef = useRef<number>(0);
    const lastPauseTimeRef = useRef<number | null>(null);
    const hasLoggedHalfway = useRef(false);
    const hasLoggedCompleted = useRef(false);

    useEffect(() => {
        isPausedRef.current = isPaused;
    }, [isPaused]);

    useEffect(() => {
        isSpeedRef.current = speed;
    }, [speed]);

    const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
        try {
            const navMeshQuery = new NavMeshQuery(navMesh);
            let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
            let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
            const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
            if (
                segmentPath.length > 0 &&
                Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
                Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
            ) {
                return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
            } else {
                console.log("There is no path here...Choose valid path")
                const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
                return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
            }
        } catch {
            console.error("Failed to compute path");
            return [];
        }
    }, [navMesh]);

    function humanStatus(modelId: string, status: string) {
        // console.log(`${modelId} , ${status}`);
    }

    function reset() {
        setCurrentPhase(human.modelUuid, 'init');
        setHumanActive(human.modelUuid, false);
        setHumanState(human.modelUuid, 'idle');
        resetAnimation(human.modelUuid);
        setPath([]);
        if (processAnimationIdRef.current) {
            cancelAnimationFrame(processAnimationIdRef.current);
            processAnimationIdRef.current = null;
        }
        processStartTimeRef.current = null;
        processTimeRef.current = 0;
        accumulatedPausedTimeRef.current = 0;
        lastPauseTimeRef.current = null;
        hasLoggedHalfway.current = false;
        hasLoggedCompleted.current = false;
        const object = scene.getObjectByProperty('uuid', human.modelUuid);
        if (object && human) {
            object.position.set(human.position[0], human.position[1], human.position[2]);
            object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
        }
    }

    useEffect(() => {
        if (isPlaying) {
            const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');

            if (!action || !(action as HumanAction).manufacturePoint || (action as HumanAction).actionType === 'worker') return;

            if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
                const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
                if (!humanMesh) return;

                const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.manufacturePoint?.position || [0, 0, 0]);
                setPath(toPickupPath);
                setHumanState(human.modelUuid, 'idle');
                setCurrentPhase(human.modelUuid, 'init-manufacture');
                setHumanActive(human.modelUuid, false);
                setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
                humanStatus(human.modelUuid, 'Human is waiting for material in manufacture');
            } else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'waiting') {
                if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
                    setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
                    setHumanState(human.modelUuid, 'running');
                    setCurrentPhase(human.modelUuid, 'manufacturing');
                    setHumanActive(human.modelUuid, true);

                    processStartTimeRef.current = performance.now();
                    processTimeRef.current = (action as HumanAction).processTime || 0;
                    accumulatedPausedTimeRef.current = 0;
                    lastPauseTimeRef.current = null;
                    hasLoggedHalfway.current = false;
                    hasLoggedCompleted.current = false;

                    if (!processAnimationIdRef.current) {
                        processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
                    }
                }
            } else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
                if ((action as HumanAction).manufacturePoint && human.currentPhase === 'manufacturing') {
                    setHumanState(human.modelUuid, 'idle');
                    setCurrentPhase(human.modelUuid, 'waiting');
                    setHumanActive(human.modelUuid, false);
                    setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
                    humanStatus(human.modelUuid, 'Human is waiting for material in manufacture');

                    decrementHumanLoad(human.modelUuid, 1);
                    const material = removeLastMaterial(human.modelUuid);
                    if (material) {
                        triggerPointActions((action as HumanAction), material.materialId);
                    }
                }
            }
        } else {
            reset()
        }
    }, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);

    const trackManufactureProcess = useCallback(() => {
        const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');

        const now = performance.now();

        if (!processStartTimeRef.current || !(action as HumanAction).processTime || !action) {
            return;
        }

        if (isPausedRef.current) {
            if (!lastPauseTimeRef.current) {
                lastPauseTimeRef.current = now;
            }
            processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
            return;
        } else if (lastPauseTimeRef.current) {
            accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
            lastPauseTimeRef.current = null;
        }

        const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
        const totalProcessTimeMs = ((action as HumanAction).processTime || 1) * 1000;

        if (elapsed >= totalProcessTimeMs / 2 && !hasLoggedHalfway.current) {
            hasLoggedHalfway.current = true;
            if (human.currentMaterials.length > 0) {
                setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
            }
            humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in manufacture.`);
        }

        if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
            hasLoggedCompleted.current = true;
            setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
            if (processAnimationIdRef.current) {
                cancelAnimationFrame(processAnimationIdRef.current);
                processAnimationIdRef.current = null;
            }
            humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed manufacture process.`);
            return;
        }

        processAnimationIdRef.current = requestAnimationFrame(trackManufactureProcess);
    }, [human.modelUuid, human.currentMaterials]);

    function handleCallBack() {
        if (human.currentPhase === 'init-manufacture') {
            setCurrentPhase(human.modelUuid, 'waiting');
            setHumanState(human.modelUuid, 'idle');
            setHumanActive(human.modelUuid, false);
            setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
            humanStatus(human.modelUuid, 'Reached manufacture point, waiting for material');
            setPath([]);
        }
    }

    return (
        <>
            <ManufacturerAnimator
                path={path}
                handleCallBack={handleCallBack}
                human={human}
                reset={reset}
            />
        </>
    )
}

export default ManufacturerInstance;