import { useCallback, useEffect, useRef, useState } from "react";
import * as THREE from "three";
import {
  ProcessData,
  ProcessAnimationState,
  SpawnedObject,
  AnimationState,
  ProcessPoint,
  PointAction,
  Trigger,
} from "./types";
import {
  useAnimationPlaySpeed,
  usePauseButtonStore,
  usePlayButtonStore,
  useResetButtonStore,
} from "../../../store/usePlayButtonStore";
import { usePlayAgv } from "../../../store/store";

// Enhanced ProcessAnimationState with trigger tracking
interface EnhancedProcessAnimationState extends ProcessAnimationState {
  triggerCounts: Record<string, number>;
  triggerLogs: Array<{
    timestamp: number;
    pointId: string;
    objectId: string;
    triggerId: string;
  }>;
}

interface ProcessContainerProps {
  processes: ProcessData[];
  setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
  agvRef: any;
}

interface PlayAgvState {
  playAgv: Record<string, any>;
  setPlayAgv: (data: any) => void;
}

export const useProcessAnimation = (
  processes: ProcessData[],
  setProcesses: React.Dispatch<React.SetStateAction<any[]>>,
  agvRef: any
) => {
  // State and refs initialization
  const { isPlaying, setIsPlaying } = usePlayButtonStore();
  const { isPaused, setIsPaused } = usePauseButtonStore();
  const { isReset, setReset } = useResetButtonStore();
  const debugRef = useRef<boolean>(false);
  const clockRef = useRef<THREE.Clock>(new THREE.Clock());
  const pauseTimeRef = useRef<number>(0);
  const elapsedBeforePauseRef = useRef<number>(0);
  const animationStatesRef = useRef<
    Record<string, EnhancedProcessAnimationState>
  >({});
  const { speed } = useAnimationPlaySpeed();
  const prevIsPlaying = useRef<boolean | null>(null);
  const [internalResetFlag, setInternalResetFlag] = useState(false);
  const [animationStates, setAnimationStates] = useState<
    Record<string, EnhancedProcessAnimationState>
  >({});
  const speedRef = useRef<number>(speed);
  const { PlayAgv, setPlayAgv } = usePlayAgv();

  // Effect hooks
  useEffect(() => {
    speedRef.current = speed;
  }, [speed]);

  useEffect(() => {
    if (prevIsPlaying.current !== null) {
      setAnimationStates({});
    }
    prevIsPlaying.current = isPlaying;
  }, [isPlaying]);

  useEffect(() => {
    animationStatesRef.current = animationStates;
  }, [animationStates]);

  // Reset handler
  useEffect(() => {
    if (isReset) {
      setInternalResetFlag(true);
      setIsPlaying(false);
      setIsPaused(false);
      setAnimationStates({});
      animationStatesRef.current = {};
      clockRef.current = new THREE.Clock();
      elapsedBeforePauseRef.current = 0;
      pauseTimeRef.current = 0;
      setReset(false);
      setTimeout(() => {
        setInternalResetFlag(false);
        setIsPlaying(true);
      }, 0);
    }
  }, [isReset, setReset, setIsPlaying, setIsPaused]);

  // Pause handler
  useEffect(() => {
    if (isPaused) {
      pauseTimeRef.current = clockRef.current.getElapsedTime();
    } else if (pauseTimeRef.current > 0) {
      const pausedDuration =
        clockRef.current.getElapsedTime() - pauseTimeRef.current;
      elapsedBeforePauseRef.current += pausedDuration;
    }
  }, [isPaused]);

  // Initialize animation states with trigger tracking
  useEffect(() => {
    if (isPlaying && !internalResetFlag) {
      const newStates: Record<string, EnhancedProcessAnimationState> = {};

      processes.forEach((process) => {
        const triggerCounts: Record<string, number> = {};

        // Initialize trigger counts for all On-Hit triggers
        process.paths?.forEach((path) => {
          path.points?.forEach((point) => {
            point.triggers?.forEach((trigger: Trigger) => {
              if (trigger.type === "On-Hit" && trigger.isUsed) {
                triggerCounts[`${point.uuid}-${trigger.uuid}`] = 0;
              }
            });
          });
        });

        newStates[process.id] = {
          spawnedObjects: {},
          nextSpawnTime: 0,
          objectIdCounter: 0,
          isProcessDelaying: false,
          processDelayStartTime: 0,
          processDelayDuration: 0,
          triggerCounts,
          triggerLogs: [],
        };
      });

      setAnimationStates(newStates);
      animationStatesRef.current = newStates;
      clockRef.current.start();
    }
  }, [isPlaying, processes, internalResetFlag]);

  // Helper functions
  const findSpawnPoint = (process: ProcessData): ProcessPoint | null => {
    for (const path of process.paths || []) {
      for (const point of path.points || []) {
        const spawnAction = point.actions?.find(
          (a) => a.isUsed && a.type === "Spawn"
        );
        if (spawnAction) {
          return point;
        }
      }
    }
    return null;
  };

  const findAnimationPathPoint = (
    process: ProcessData,
    spawnPoint: ProcessPoint
  ): THREE.Vector3 => {
    if (process.animationPath && process.animationPath.length > 0) {
      let pointIndex = 0;
      for (const path of process.paths || []) {
        for (let i = 0; i < (path.points?.length || 0); i++) {
          const point = path.points?.[i];
          if (point && point.uuid === spawnPoint.uuid) {
            if (process.animationPath[pointIndex]) {
              const p = process.animationPath[pointIndex];
              return new THREE.Vector3(p.x, p.y, p.z);
            }
          }
          pointIndex++;
        }
      }
    }
    return new THREE.Vector3(
      spawnPoint.position[0],
      spawnPoint.position[1],
      spawnPoint.position[2]
    );
  };

  // Optimized object creation
  const createSpawnedObject = useCallback(
    (
      process: ProcessData,
      currentTime: number,
      materialType: string,
      spawnPoint: ProcessPoint,
      baseMaterials: Record<string, THREE.Material>
    ): SpawnedObject => {
      const processMaterials = {
        ...baseMaterials,
        ...(process.customMaterials || {}),
      };

      const spawnPosition = findAnimationPathPoint(process, spawnPoint);
      const material =
        processMaterials[materialType as keyof typeof processMaterials] ||
        baseMaterials.Default;

      return {
        ref: { current: null },
        state: {
          currentIndex: 0,
          progress: 0,
          isAnimating: true,
          speed: process.speed || 1,
          isDelaying: false,
          delayStartTime: 0,
          currentDelayDuration: 0,
          delayComplete: false,
          currentPathIndex: 0,
        },
        visible: true,
        material: material,
        currentMaterialType: materialType,
        spawnTime: currentTime,
        position: spawnPosition,
      };
    },
    []
  );

  // Material handling
  const handleMaterialSwap = useCallback(
    (
      processId: string,
      objectId: string,
      materialType: string,
      processes: ProcessData[],
      baseMaterials: Record<string, THREE.Material>
    ) => {
      setAnimationStates((prev) => {
        const processState = prev[processId];
        if (!processState || !processState.spawnedObjects[objectId])
          return prev;

        const process = processes.find((p) => p.id === processId);
        if (!process) return prev;

        const processMaterials = {
          ...baseMaterials,
          ...(process.customMaterials || {}),
        };

        const newMaterial =
          processMaterials[materialType as keyof typeof processMaterials];
        if (!newMaterial) return prev;

        return {
          ...prev,
          [processId]: {
            ...processState,
            spawnedObjects: {
              ...processState.spawnedObjects,
              [objectId]: {
                ...processState.spawnedObjects[objectId],
                material: newMaterial,
                currentMaterialType: materialType,
              },
            },
          },
        };
      });
    },
    []
  );

  // Point action handler with trigger counting
  const handlePointActions = useCallback(
    (
      processId: string,
      objectId: string,
      actions: PointAction[] = [],
      currentTime: number,
      processes: ProcessData[],
      baseMaterials: Record<string, THREE.Material>
    ): boolean => {
      let shouldStopAnimation = false;

      actions.forEach((action) => {
        if (!action.isUsed) return;

        switch (action.type) {
          case "Delay":
            setAnimationStates((prev) => {
              const processState = prev[processId];
              if (!processState || processState.isProcessDelaying) return prev;

              const delayDuration =
                typeof action.delay === "number"
                  ? action.delay
                  : parseFloat(action.delay as string) || 0;

              if (delayDuration > 0) {
                return {
                  ...prev,
                  [processId]: {
                    ...processState,
                    isProcessDelaying: true,
                    processDelayStartTime: currentTime,
                    processDelayDuration: delayDuration,
                    spawnedObjects: {
                      ...processState.spawnedObjects,
                      [objectId]: {
                        ...processState.spawnedObjects[objectId],
                        state: {
                          ...processState.spawnedObjects[objectId].state,
                          isAnimating: false,
                          isDelaying: true,
                          delayStartTime: currentTime,
                          currentDelayDuration: delayDuration,
                          delayComplete: false,
                        },
                      },
                    },
                  },
                };
              }
              return prev;
            });
            shouldStopAnimation = true;
            break;

          case "Despawn":
            setAnimationStates((prev) => {
              const processState = prev[processId];
              if (!processState) return prev;

              const newSpawnedObjects = { ...processState.spawnedObjects };
              delete newSpawnedObjects[objectId];

              return {
                ...prev,
                [processId]: {
                  ...processState,
                  spawnedObjects: newSpawnedObjects,
                },
              };
            });
            shouldStopAnimation = true;
            break;

          case "Swap":
            if (action.material) {
              handleMaterialSwap(
                processId,
                objectId,
                action.material,
                processes,
                baseMaterials
              );
            }
            break;

          default:
            break;
        }
      });

      return shouldStopAnimation;
    },
    [handleMaterialSwap]
  );

  // Trigger counting system
  const checkAndCountTriggers = useCallback(
    (
      processId: string,
      objectId: string,
      currentPointIndex: number,
      processes: ProcessData[],
      currentTime: number
    ) => {
      setAnimationStates((prev) => {
        const processState = prev[processId];
        if (!processState) return prev;

        const process = processes.find((p) => p.id === processId);
        if (!process) return prev;

        const point = getPointDataForAnimationIndex(process, currentPointIndex);
        if (!point?.triggers) return prev;

        const onHitTriggers = point.triggers.filter(
          (t: Trigger) => t.type === "On-Hit" && t.isUsed
        );
        if (onHitTriggers.length === 0) return prev;

        const newTriggerCounts = { ...processState.triggerCounts };
        const newTriggerLogs = [...processState.triggerLogs];
        let shouldLog = false;

        // Update counts for all valid triggers
        onHitTriggers.forEach((trigger: Trigger) => {
          const triggerKey = `${point.uuid}-${trigger.uuid}`;
          newTriggerCounts[triggerKey] =
            (newTriggerCounts[triggerKey] || 0) + 1;
          shouldLog = true;
          newTriggerLogs.push({
            timestamp: currentTime,
            pointId: point.uuid,
            objectId,
            triggerId: trigger.uuid,
          });
        });

        const processTotalHits = Object.values(newTriggerCounts).reduce(
          (a, b) => a + b,
          0
        );

        if (shouldLog) {
          const vehiclePaths = process.paths.filter(
            (path) => path.type === "Vehicle"
          );

          vehiclePaths.forEach((vehiclePath) => {
            if (vehiclePath.points?.length > 0) {
              const vehiclePoint = vehiclePath.points[0];
              const action = vehiclePoint.actions?.[0];
              let expectedHitCount = action?.hitCount;

              if (expectedHitCount !== undefined) {
                const vehicleId = vehiclePath.modeluuid;

                let vehicleEntry = agvRef.current.find(
                  (v: any) =>
                    v.vehicleId === vehicleId && v.processId === processId
                );

                if (!vehicleEntry) {
                  vehicleEntry = {
                    processId,
                    vehicleId,
                    expectedHitCount,
                    isplaying: false,
                    hitCount: 0, // Initialize hitCount
                  };
                  agvRef.current.push(vehicleEntry);
                }

                // Increment expectedHitCount if not playing
                if (!vehicleEntry.isplaying) {
                  vehicleEntry.expectedHitCount = processTotalHits;
                }

                // Set vehicle's hitCount to the processTotalHits
                vehicleEntry.hitCount = processTotalHits;
                vehicleEntry.lastUpdated = currentTime;

                // If hitCount matches expectedHitCount, set isplaying to true
                if (vehicleEntry.hitCount >= vehicleEntry.expectedHitCount) {
                  vehicleEntry.isplaying = true;
                }
              }
            }
          });
        }

        return {
          ...prev,
          [processId]: {
            ...processState,
            triggerCounts: newTriggerCounts,
            triggerLogs: newTriggerLogs,
            totalHits: processTotalHits,
          },
        };
      });
    },
    []
  );

  // Utility functions
  const hasNonInheritActions = useCallback(
    (actions: PointAction[] = []): boolean => {
      return actions.some(
        (action) => action.isUsed && action.type !== "Inherit"
      );
    },
    []
  );

  const getPointDataForAnimationIndex = useCallback(
    (process: ProcessData, index: number): ProcessPoint | null => {
      if (!process.paths) return null;

      let cumulativePoints = 0;
      for (const path of process.paths) {
        const pointCount = path.points?.length || 0;

        if (index < cumulativePoints + pointCount) {
          const pointIndex = index - cumulativePoints;
          return path.points?.[pointIndex] || null;
        }

        cumulativePoints += pointCount;
      }

      return null;
    },
    []
  );

  const getTriggerCounts = useCallback((processId: string) => {
    return animationStatesRef.current[processId]?.triggerCounts || {};
  }, []);

  const getTriggerLogs = useCallback((processId: string) => {
    return animationStatesRef.current[processId]?.triggerLogs || [];
  }, []);

  return {
    animationStates,
    setAnimationStates,
    clockRef,
    elapsedBeforePauseRef,
    speedRef,
    debugRef,
    findSpawnPoint,
    createSpawnedObject,
    handlePointActions,
    hasNonInheritActions,
    getPointDataForAnimationIndex,
    checkAndCountTriggers,
    getTriggerCounts,
    getTriggerLogs,
    processes,
  };
};
