import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import { useRef, useState, useEffect, useMemo } from "react";
import { Sphere, TransformControls } from "@react-three/drei";
import {
	useEditingPoint,
	useEyeDropMode,
	useIsConnecting,
	usePreviewPosition,
	useRenderDistance,
	useSelectedActionSphere,
	useSelectedPath,
	useSimulationStates,
} from "../../../store/store";
import { useFrame, useThree } from "@react-three/fiber";
import { useSubModuleStore } from "../../../store/useModuleStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt";

function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject<THREE.Group>; }) {
	const { isPlaying } = usePlayButtonStore();
	const { renderDistance } = useRenderDistance();
	const { setSubModule } = useSubModuleStore();
	const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere();
	const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
	const { editingPoint, setEditingPoint } = useEditingPoint();
	const { previewPosition, setPreviewPosition } = usePreviewPosition();
	const { raycaster, camera, pointer, gl } = useThree();
	const { setSelectedPath } = useSelectedPath();
	const { simulationStates, setSimulationStates } = useSimulationStates();
	const { isConnecting } = useIsConnecting();
	const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);

	const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
	const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
	const isMovingRef = useRef(false);
	const transformRef = useRef<any>(null);
	const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);

	useEffect(() => {
		setTransformMode(null);
		const handleKeyDown = (e: KeyboardEvent) => {
			if (!selectedActionSphere) return;
			if (e.key === "g") {
				setTransformMode((prev) => (prev === "translate" ? null : "translate"));
			}
			if (e.key === "r") {
				setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
			}
		};

		window.addEventListener("keydown", handleKeyDown);
		return () => window.removeEventListener("keydown", handleKeyDown);
	}, [selectedActionSphere]);

	useFrame(() => {
		Object.values(groupRefs.current).forEach((group) => {
			if (group) {
				const distance = new THREE.Vector3(
					...group.position.toArray()
				).distanceTo(camera.position);
				group.visible = ((distance <= renderDistance) && !isPlaying);
			}
		});
	});

	const updateSimulationPaths = () => {
		if (!selectedActionSphere) return;

		const updatedPaths = simulationStates.map((path) => {
			if (path.type === "Conveyor") {
				return {
					...path,
					points: path.points.map((point) =>
						point.uuid === selectedActionSphere.points.uuid
							? {
								...point,
								position: [
									selectedActionSphere.points.position.x,
									selectedActionSphere.points.position.y,
									selectedActionSphere.points.position.z,
								],
								rotation: [
									selectedActionSphere.points.rotation.x,
									selectedActionSphere.points.rotation.y,
									selectedActionSphere.points.rotation.z,
								],
							}
							: point
					),
				};
			} else {
				return path;
			}
		}) as Types.ConveyorEventsSchema[];

		const updatedPath = updatedPaths.find(
			(path) => path.type === "Conveyor" && path.points.some((point) => point.uuid === selectedActionSphere.points.uuid)
		);

		// console.log("Updated Path:", updatedPath);

		setSimulationStates(updatedPaths);
	};

	useFrame(() => {
		if (eyeDropMode) {
			raycaster.setFromCamera(pointer, camera);
			const intersectionPoint = new THREE.Vector3();
			const point = raycaster.ray.intersectPlane(plane, intersectionPoint);

			if (point) {
				setPreviewPosition({ x: point.x, y: point.z });
			}
		} else {
			setPreviewPosition(null);
		}
	});

	useEffect(() => {
		if (!camera) return;
		const canvasElement = gl.domElement;
		canvasElement.tabIndex = 0;

		const onPointerDown = () => {
			isMovingRef.current = false;
		};

		const onPointerMove = () => {
			isMovingRef.current = true;
		};

		const onPointerUp = (event: PointerEvent) => {
			if (
				!isMovingRef.current &&
				eyeDropMode &&
				event.button === 0 &&
				previewPosition
			) {
				event.preventDefault();
				if (editingPoint) {
					handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y);
					setEditingPoint(null);
					setEyeDropMode(false);
				}
			}
		};

		if (eyeDropMode) {
			canvasElement.addEventListener("pointerdown", onPointerDown);
			canvasElement.addEventListener("pointermove", onPointerMove);
			canvasElement.addEventListener("pointerup", onPointerUp);
		}

		return () => {
			canvasElement.removeEventListener("pointerdown", onPointerDown);
			canvasElement.removeEventListener("pointermove", onPointerMove);
			canvasElement.removeEventListener("pointerup", onPointerUp);
		};
	}, [eyeDropMode, editingPoint, previewPosition]);

	const updateBackend = async (updatedPath: Types.VehicleEventsSchema | undefined) => {
		if (!updatedPath) return;
		const email = localStorage.getItem("email");
		const organization = email ? email.split("@")[1].split(".")[0] : "";
		await setEventApi(
			organization,
			updatedPath.modeluuid,
			{ type: "Vehicle", points: updatedPath.points }
		);
	}

	const handlePointUpdate = (pointType: "start" | "end", x: number, z: number) => {
		if (!selectedActionSphere?.points?.uuid) return;
		const updatedPaths = simulationStates.map((path) => {

			if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) {
				return {
					...path,
					points: {
						...path.points,
						actions: {
							...path.points.actions,
							[pointType]: { ...path.points.actions[pointType], x: x, y: z, },
						},
					},
				};
			}
			return path;
		});

		const updatedPath = updatedPaths.find((path): path is Types.VehicleEventsSchema => path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid);
		updateBackend(updatedPath);

		setSimulationStates(updatedPaths);
	};

	return (
		<group visible={!isPlaying} name="simulation-simulationStates-group" ref={pathsGroupRef}>
			{simulationStates.map((path) => {
				if (path.type === "Conveyor") {
					const points = path.points.map(
						(point) => new THREE.Vector3(...point.position)
					);

					return (
						<group
							name={`${path.modeluuid}-${path.type}-path`}
							uuid={path.modeluuid}
							key={path.modeluuid}
							ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
							position={path.position}
							rotation={path.rotation}
							onClick={(e) => {
								if (isConnecting || eyeDropMode) return;
								e.stopPropagation();
								setSelectedPath({
									path,
									group: groupRefs.current[path.modeluuid],
								});
								setSelectedActionSphere(null);
								setTransformMode(null);
								setSubModule("mechanics");
							}}
							onPointerMissed={() => {
								if (eyeDropMode) return;
								setSelectedPath(null);
								setSubModule("properties");
							}}
						>
							{path.points.map((point, index) => (
								<Sphere
									key={point.uuid}
									uuid={point.uuid}
									position={point.position}
									args={[0.15, 32, 32]}
									name="events-sphere"
									ref={(el) => (sphereRefs.current[point.uuid] = el!)}
									onClick={(e) => {
										if (isConnecting || eyeDropMode) return;
										e.stopPropagation();
										setSelectedActionSphere({
											path,
											points: sphereRefs.current[point.uuid],
										});
										setSubModule("mechanics");
										setSelectedPath(null);
									}}
									userData={{ points, path }}
									onPointerMissed={() => {
										if (eyeDropMode) return;
										setSubModule("properties");
										setSelectedActionSphere(null);
									}}
								>
									<meshStandardMaterial
										color={index === 0 ? "orange" : index === path.points.length - 1 ? "blue" : "green"}
									/>
								</Sphere>
							))}

							{points.slice(0, -1).map((point, index) => {
								const nextPoint = points[index + 1];
								const segmentCurve = new THREE.CatmullRomCurve3([point, nextPoint,]);
								const tubeGeometry = new THREE.TubeGeometry(segmentCurve, 20, 0.1, 16, false);

								return (
									<mesh name="event-connection-tube" key={`tube-${index}`} geometry={tubeGeometry}>
										<meshStandardMaterial transparent opacity={0.9} color="red" />
									</mesh>
								);
							})}
						</group>
					);
				} else if (path.type === "Vehicle") {
					return (
						<group
							name={`${path.modeluuid}-${path.type}-path`}
							uuid={path.modeluuid}
							key={path.modeluuid}
							ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
							position={path.position}
							onClick={(e) => {
								if (isConnecting || eyeDropMode) return;
								e.stopPropagation();
								setSelectedPath({
									path,
									group: groupRefs.current[path.modeluuid],
								});
								setSelectedActionSphere(null);
								setTransformMode(null);
								setSubModule("mechanics");
							}}
							onPointerMissed={() => {
								if (eyeDropMode) return;
								setSelectedPath(null);
								setSubModule("properties");
							}}
						>
							<Sphere
								key={path.points.uuid}
								uuid={path.points.uuid}
								position={path.points.position}
								args={[0.15, 32, 32]}
								name="events-sphere"
								ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
								onClick={(e) => {
									if (isConnecting || eyeDropMode) return;
									e.stopPropagation();
									setSelectedActionSphere({
										path,
										points: sphereRefs.current[path.points.uuid],
									});
									setSubModule("mechanics");
									setSelectedPath(null);
								}}
								userData={{ points: path.points, path }}
								onPointerMissed={() => {
									if (eyeDropMode) return;
									setSubModule("properties");
									setSelectedActionSphere(null);
								}}
							>
								<meshStandardMaterial color="purple" />
							</Sphere>
						</group>
					);
				} else if (path.type === "StaticMachine") {
					return (
						<group
							name={`${path.modeluuid}-${path.type}-path`}
							uuid={path.modeluuid}
							key={path.modeluuid}
							ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
							position={path.position}
							rotation={path.rotation}
							onClick={(e) => {
								if (isConnecting || eyeDropMode) return;
								e.stopPropagation();
								setSelectedPath({
									path,
									group: groupRefs.current[path.modeluuid],
								});
								setSelectedActionSphere(null);
								setTransformMode(null);
								setSubModule("mechanics");
							}}
							onPointerMissed={() => {
								if (eyeDropMode) return;
								setSelectedPath(null);
								setSubModule("properties");
							}}
						>
							<Sphere
								key={path.points.uuid}
								uuid={path.points.uuid}
								position={path.points.position}
								args={[0.15, 32, 32]}
								name="events-sphere"
								ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
								onClick={(e) => {
									if (isConnecting || eyeDropMode) return;
									e.stopPropagation();
									setSelectedActionSphere({
										path,
										points: sphereRefs.current[path.points.uuid],
									});
									setSubModule("mechanics");
									setSelectedPath(null);
								}}
								userData={{ points: path.points, path }}
								onPointerMissed={() => {
									if (eyeDropMode) return;
									setSubModule("properties");
									setSelectedActionSphere(null);
								}}
							>
								<meshStandardMaterial color="yellow" />
							</Sphere>
						</group>
					);
				} else if (path.type === "ArmBot") {
					return (
						<group
							name={`${path.modeluuid}-${path.type}-path`}
							uuid={path.modeluuid}
							key={path.modeluuid}
							ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
							position={path.position}
							rotation={path.rotation}
							onClick={(e) => {
								if (isConnecting || eyeDropMode) return;
								e.stopPropagation();
								setSelectedPath({
									path,
									group: groupRefs.current[path.modeluuid],
								});
								setSelectedActionSphere(null);
								setTransformMode(null);
								setSubModule("mechanics");
							}}
							onPointerMissed={() => {
								if (eyeDropMode) return;
								setSelectedPath(null);
								setSubModule("properties");
							}}
						>
							<Sphere
								key={path.points.uuid}
								uuid={path.points.uuid}
								position={path.points.position}
								args={[0.15, 32, 32]}
								name="events-sphere"
								ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
								onClick={(e) => {
									if (isConnecting || eyeDropMode) return;
									e.stopPropagation();
									setSelectedActionSphere({
										path,
										points: sphereRefs.current[path.points.uuid],
									});
									setSubModule("mechanics");
									setSelectedPath(null);
								}}
								userData={{ points: path.points, path }}
								onPointerMissed={() => {
									if (eyeDropMode) return;
									setSubModule("properties");
									setSelectedActionSphere(null);
								}}
							>
								<meshStandardMaterial color="pink" />
							</Sphere>
						</group>
					);
				}
				return null;
			})}

			{selectedActionSphere && transformMode && (
				<TransformControls
					ref={transformRef}
					object={selectedActionSphere.points}
					mode={transformMode}
					onMouseUp={updateSimulationPaths}
				/>
			)}
		</group>
	);
}

export default PathCreation;
