import { useCallback, useEffect, useState } from "react";
import * as THREE from 'three';
import { useFrame } from "@react-three/fiber";
import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore";
import { useSceneContext } from "../../../../scene/sceneContext";
import { useProductContext } from "../../../products/productContext";

interface SpawnInstance {
    lastSpawnTime: number | null;
    startTime: number;
    spawnCount: number;
    params: {
        material: string;
        intervalMs: number;
        totalCount: number;
        action: ConveyorAction;
    };
    pauseStartTime: number;
    remainingTime: number;
    isPaused: boolean;
}

export function useSpawnHandler() {
    const { materialStore, conveyorStore, productStore } = useSceneContext();
    const { addMaterial } = materialStore();
    const { getConveyorById } = conveyorStore();
    const { getModelUuidByActionUuid, getPointUuidByActionUuid } = productStore();
    const { isPlaying } = usePlayButtonStore();
    const { isPaused } = usePauseButtonStore();
    const { speed } = useAnimationPlaySpeed();
    const { isReset } = useResetButtonStore();
    const { selectedProductStore } = useProductContext();
    const { selectedProduct } = selectedProductStore();

    const [activeSpawns, setActiveSpawns] = useState<Map<string, SpawnInstance>>(new Map());

    const getConveyorPausedState = useCallback((action: ConveyorAction) => {
        const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
        if (!modelUuid) return false;

        const conveyor = getConveyorById(modelUuid);
        if (!conveyor) return false;
        return conveyor.isPaused;
    }, [getConveyorById, getModelUuidByActionUuid, selectedProduct.productUuid]);

    const shouldPauseSpawn = useCallback((action: ConveyorAction) => {
        return isPaused || getConveyorPausedState(action);
    }, [isPaused, getConveyorPausedState]);

    const clearAllSpawns = useCallback(() => {
        setActiveSpawns(new Map());
    }, []);

    useEffect(() => {
        if (isReset) {
            clearAllSpawns();
        }
    }, [isReset, clearAllSpawns]);

    const spawnLogStatus = (materialUuid: string, status: string) => {
        echo.info(`${materialUuid}, ${status}`);
    }

    const createNewMaterial = useCallback((materialType: string, action: ConveyorAction) => {
        const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
        const pointUuid = getPointUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
        if (!modelUuid || !pointUuid) return;
        const currentTime = performance.now();

        const newMaterial: MaterialSchema = {
            materialId: THREE.MathUtils.generateUUID(),
            materialName: `${materialType}-${Date.now()}`,
            materialType: materialType,
            isActive: false,
            isVisible: true,
            isPaused: false,
            isRendered: true,
            startTime: currentTime,
            current: {
                modelUuid: modelUuid,
                pointUuid: pointUuid,
                actionUuid: action.actionUuid
            },
        };

        if (action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
            action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid &&
            action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid
        ) {
            newMaterial.next = {
                modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid,
                pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid,
            }
        }

        addMaterial(newMaterial);
        return newMaterial;
    }, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productUuid]);

    useFrame(() => {
        const currentTime = performance.now();
        const completedActions: string[] = [];
        let hasChanges = false;

        activeSpawns.forEach(spawn => {
            const isPausedNow = shouldPauseSpawn(spawn.params.action);

            if (isPausedNow && !spawn.isPaused) {
                if (spawn.lastSpawnTime === null) {
                    spawn.remainingTime = Math.max(0, (spawn.params.intervalMs / speed) - (currentTime - spawn.startTime));
                } else {
                    spawn.remainingTime = Math.max(0, (spawn.params.intervalMs / speed) - (currentTime - spawn.lastSpawnTime));
                }
                spawn.pauseStartTime = currentTime;
                spawn.isPaused = true;
                hasChanges = true;
            } else if (!isPausedNow && spawn.isPaused) {
                const pauseDuration = currentTime - spawn.pauseStartTime;
                if (spawn.lastSpawnTime === null) {
                    spawn.startTime += pauseDuration;
                } else {
                    spawn.lastSpawnTime += pauseDuration;
                }
                spawn.isPaused = false;
                spawn.pauseStartTime = 0;
                spawn.remainingTime = 0;
                hasChanges = true;
            }
        });

        if (isPlaying && !isReset && !isPaused) {
            activeSpawns.forEach((spawn, actionUuid) => {
                if (spawn.isPaused) return;

                const { material, intervalMs, totalCount, action } = spawn.params;
                const adjustedInterval = intervalMs / speed;
                const isFirstSpawn = spawn.lastSpawnTime === null;

                // First spawn
                if (isFirstSpawn) {
                    const elapsed = currentTime - spawn.startTime;
                    if (elapsed >= adjustedInterval) {
                        const createdMaterial = createNewMaterial(material, action);
                        if (createdMaterial) {
                            spawnLogStatus(createdMaterial.materialName, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`);
                        }
                        spawn.lastSpawnTime = currentTime;
                        spawn.spawnCount = 1;
                        hasChanges = true;

                        if (totalCount <= 1) {
                            completedActions.push(actionUuid);
                        }
                    }
                    return;
                }

                // Subsequent spawns
                if (spawn.lastSpawnTime !== null) {
                    const timeSinceLast = currentTime - spawn.lastSpawnTime;
                    if (timeSinceLast >= adjustedInterval) {
                        const count = spawn.spawnCount + 1;
                        const createdMaterial = createNewMaterial(material, action);
                        if (createdMaterial) {
                            spawnLogStatus(createdMaterial.materialName, `[${timeSinceLast.toFixed(2)}ms] Spawned ${material} (${count}/${totalCount})`);
                        }
                        spawn.lastSpawnTime = currentTime;
                        spawn.spawnCount = count;
                        hasChanges = true;

                        if (count >= totalCount) {
                            completedActions.push(actionUuid);
                        }
                    }
                }
            });
        }

        if (hasChanges || completedActions.length > 0) {
            setActiveSpawns(prevSpawns => {
                const newSpawns = new Map(prevSpawns);

                completedActions.forEach(actionUuid => {
                    newSpawns.delete(actionUuid);
                });

                return newSpawns;
            });
        }
    });

    const handleSpawn = useCallback((action: ConveyorAction) => {
        if (!action || action.actionType !== 'spawn') return;

        const { material, spawnInterval = 0, spawnCount = 1, actionUuid } = action;
        const intervalMs = spawnInterval * 1000;

        setActiveSpawns(prevSpawns => {
            const newSpawns = new Map(prevSpawns);

            if (newSpawns.has(actionUuid)) {
                newSpawns.delete(actionUuid);
            }

            newSpawns.set(actionUuid, {
                lastSpawnTime: null,
                startTime: performance.now(),
                spawnCount: 0,
                params: {
                    material,
                    intervalMs,
                    totalCount: spawnCount,
                    action: action
                },
                pauseStartTime: 0,
                remainingTime: 0,
                isPaused: false
            });

            return newSpawns;
        });
    }, []);

    useEffect(() => {
        return () => {
            clearAllSpawns();
        };
    }, [clearAllSpawns]);

    return {
        handleSpawn,
        clearAllSpawns
    };
}