import React, { useRef, useState, useEffect, useMemo } from "react";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { GLTFLoader } from "three-stdlib";
import { useLoader, useFrame } from "@react-three/fiber";
import * as THREE from "three";
import { GLTF } from "three-stdlib";
import boxGltb from "../../../assets/gltf-glb/crate_box.glb";
import camera from "../../../assets/gltf-glb/camera face 2.gltf";

interface PointAction {
  uuid: string;
  name: string;
  type: "Inherit" | "Spawn" | "Despawn" | "Delay" | "Swap";
  objectType: string;
  material: string;
  delay: string | number;
  spawnInterval: string | number;
  isUsed: boolean;
}

interface ProcessPoint {
  uuid: string;
  position: number[];
  rotation: number[];
  actions: PointAction[];
  connections: {
    source: { pathUUID: string; pointUUID: string };
    targets: { pathUUID: string; pointUUID: string }[];
  };
}

interface ProcessPath {
  modeluuid: string;
  modelName: string;
  points: ProcessPoint[];
  pathPosition: number[];
  pathRotation: number[];
  speed: number;
}

interface ProcessData {
  id: string;
  paths: ProcessPath[];
  animationPath: { x: number; y: number; z: number }[];
  pointActions: PointAction[][];
  speed: number;
}

interface AnimationState {
  currentIndex: number;
  progress: number;
  isAnimating: boolean;
  speed: number;
  isDelaying: boolean;
  delayStartTime: number;
  currentDelayDuration: number;
  delayComplete: boolean;
  currentPathIndex: number;
}

const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({
  processes,
}) => {
  console.log("processes: ", processes);
  const gltf = useLoader(GLTFLoader, boxGltb) as GLTF;
  const { isPlaying, setIsPlaying } = usePlayButtonStore();

  const groupRef = useRef<THREE.Group>(null);
  const meshRef = useRef<THREE.Mesh>(null);
  const [visible, setVisible] = useState(false);
  const [currentPathIndex, setCurrentPathIndex] = useState(0);

  const materials = useMemo(
    () => ({
      Wood: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
      Box: new THREE.MeshStandardMaterial({
        color: 0xcccccc,
        metalness: 0.8,
        roughness: 0.2,
      }),
      Crate: new THREE.MeshStandardMaterial({
        color: 0x00aaff,
        metalness: 0.1,
        roughness: 0.5,
      }),
      Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
    }),
    []
  );

  const [currentMaterial, setCurrentMaterial] = useState<THREE.Material>(
    materials.Default
  );

  const { animationPath, currentProcess } = useMemo(() => {
    const defaultProcess = {
      animationPath: [],
      pointActions: [],
      speed: 1,
      paths: [],
    };
    const cp = processes?.[0] || defaultProcess;
    return {
      animationPath:
        cp.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) || [],
      currentProcess: cp,
    };
  }, [processes]);

  const animationStateRef = useRef<AnimationState>({
    currentIndex: 0,
    progress: 0,
    isAnimating: false,
    speed: currentProcess.speed,
    isDelaying: false,
    delayStartTime: 0,
    currentDelayDuration: 0,
    delayComplete: false,
    currentPathIndex: 0,
  });

  const getPointDataForAnimationIndex = (index: number) => {
    if (!processes[0]?.paths) return null;

    let cumulativePoints = 0;
    console.log("cumulativePoints: ", cumulativePoints);

    for (const path of processes[0].paths) {
      const pointCount = path.points?.length || 0;

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

      cumulativePoints += pointCount;
    }

    return null;
  };

  useEffect(() => {
    if (isPlaying) {
      setVisible(true);
      animationStateRef.current = {
        currentIndex: 0,
        progress: 0,
        isAnimating: true,
        speed: currentProcess.speed,
        isDelaying: false,
        delayStartTime: 0,
        currentDelayDuration: 0,
        delayComplete: false,
        currentPathIndex: 0,
      };

      const currentRef = gltf?.scene ? groupRef.current : meshRef.current;
      if (currentRef && animationPath.length > 0) {
        currentRef.position.copy(animationPath[0]);
      }
    } else {
      animationStateRef.current.isAnimating = false;
    }
  }, [isPlaying, currentProcess, animationPath]);

  const handleMaterialSwap = (materialType: string) => {
    setCurrentMaterial(
      materials[materialType as keyof typeof materials] || materials.Default
    );
  };

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

  const handlePointActions = (
    actions: PointAction[] = [],
    currentTime: number
  ) => {
    let shouldStopAnimation = false;

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

      switch (action.type) {
        case "Delay":
          if (
            !animationStateRef.current.isDelaying &&
            !animationStateRef.current.delayComplete
          ) {
            const delayDuration =
              typeof action.delay === "number"
                ? action.delay
                : parseFloat(action.delay as string) || 0;

            if (delayDuration > 0) {
              animationStateRef.current.isDelaying = true;
              animationStateRef.current.delayStartTime = currentTime;
              animationStateRef.current.currentDelayDuration = delayDuration;
              shouldStopAnimation = true;
            }
          }
          break;

        case "Despawn":
          setVisible(false);
          setIsPlaying(false);
          animationStateRef.current.isAnimating = false;
          shouldStopAnimation = true;
          break;

        case "Spawn":
          setVisible(true);
          break;

        case "Swap":
          if (action.material) {
            handleMaterialSwap(action.material);
          }
          break;

        case "Inherit":
          // Handle inherit logic if needed
          break;
      }
    });

    return shouldStopAnimation;
  };

  useFrame((state, delta) => {
    const currentRef = gltf?.scene ? groupRef.current : meshRef.current;
    if (
      !currentRef ||
      !animationStateRef.current.isAnimating ||
      animationPath.length < 2
    ) {
      return;
    }

    const currentTime = state.clock.getElapsedTime();
    const path = animationPath;
    const stateRef = animationStateRef.current;

    // Check if we need to switch paths (specific to your structure)
    if (stateRef.currentIndex === 3 && stateRef.currentPathIndex === 0) {
      setCurrentPathIndex(1);
      stateRef.currentPathIndex = 1;
    }

    // Get the current point data
    const currentPointData = getPointDataForAnimationIndex(
      stateRef.currentIndex
    );

    // Execute actions when we first arrive at a point

    if (stateRef.progress === 0 && currentPointData?.actions) {
      console.log(
        `Processing actions at point ${stateRef.currentIndex}`,
        currentPointData.actions
      );
      const shouldStop = handlePointActions(
        currentPointData.actions,
        currentTime
      );
      if (shouldStop) return;
    }

    // Handle delays
    if (stateRef.isDelaying) {
      console.log(
        `Delaying... ${currentTime - stateRef.delayStartTime}/${
          stateRef.currentDelayDuration
        }`
      );
      if (
        currentTime - stateRef.delayStartTime >=
        stateRef.currentDelayDuration
      ) {
        stateRef.isDelaying = false;
        stateRef.delayComplete = true;
      } else {
        return; // Keep waiting
      }
    }

    const nextPointIdx = stateRef.currentIndex + 1;
    const isLastPoint = nextPointIdx >= path.length;

    if (isLastPoint) {
      if (currentPointData?.actions) {
        const shouldStop = !hasNonInheritActions(currentPointData.actions);
        if (shouldStop) {
          currentRef.position.copy(path[stateRef.currentIndex]);
          setIsPlaying(false);
          stateRef.isAnimating = false;
          return;
        }
      }
    }

    if (!isLastPoint) {
      const nextPoint = path[nextPointIdx];
      const distance = path[stateRef.currentIndex].distanceTo(nextPoint);
      const movement = stateRef.speed * delta;
      stateRef.progress += movement / distance;

      if (stateRef.progress >= 1) {
        stateRef.currentIndex = nextPointIdx;
        stateRef.progress = 0;
        stateRef.delayComplete = false;
        currentRef.position.copy(nextPoint);
      } else {
        currentRef.position.lerpVectors(
          path[stateRef.currentIndex],
          nextPoint,
          stateRef.progress
        );
      }
    }
  });

  if (!processes || processes.length === 0) {
    return null;
  }

  if (!gltf?.scene) {
    return visible ? (
      <mesh ref={meshRef} material={currentMaterial}>
        <boxGeometry args={[1, 1, 1]} />
      </mesh>
    ) : null;
  }

  return visible ? (
    <group ref={groupRef}>
      <primitive
        object={gltf.scene.clone()}
        scale={[1, 1, 1]}
        material={currentMaterial}
      />
    </group>
  ) : null;
};

export default ProcessAnimator;
