import * as THREE from "three";
import { useEffect, useRef, useState } from "react";
import { useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import camModel from "../../../assets/gltf-glb/camera face 2.gltf";
import getActiveUsersData from "../../../services/factoryBuilder/collab/getActiveUsers";
import { useActiveUsers, useSocketStore } from "../../../store/store";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { useNavigate } from "react-router-dom";
import { Html } from "@react-three/drei";
import CollabUserIcon from "../../../functions/collabUserIcon";
import { getAvatarColor } from "../../../functions/users/functions/getAvatarColor";
import useModuleStore from "../../../store/useModuleStore";

const CamModelsGroup = () => {
	const navigate = useNavigate();
	const groupRef = useRef<THREE.Group>(null);
	const email = localStorage.getItem("email");
	const { setActiveUsers } = useActiveUsers();
	const { socket } = useSocketStore();
	const { activeModule } = useModuleStore();

	const loader = new GLTFLoader();
	const dracoLoader = new DRACOLoader();
	dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
	loader.setDRACOLoader(dracoLoader);

	const [cams, setCams] = useState<any[]>([]);
	const [models, setModels] = useState<Record<string, { targetPosition: THREE.Vector3; targetRotation: THREE.Euler }>>({});

	const dedupeCams = (cams: any[]) => {
		const seen = new Set();
		return cams.filter((cam) => {
			if (seen.has(cam.uuid)) return false;
			seen.add(cam.uuid);
			return true;
		});
	};

	const dedupeUsers = (users: any[]) => {
		const seen = new Set();
		return users.filter((user) => {
			if (seen.has(user._id)) return false;
			seen.add(user._id);
			return true;
		});
	};

	useEffect(() => {
		if (!email) navigate("/");

		if (!socket) return;
		const organization = email!.split("@")[1].split(".")[0];

		socket.on("userConnectResponse", (data: any) => {
			if (!groupRef.current) return;
			if (data.data.userData.email === email) return;
			if (socket.id === data.socketId || organization !== data.organization)
				return;

			const model = groupRef.current.getObjectByProperty(
				"uuid",
				data.data.userData._id
			);
			if (model) {
				groupRef.current.remove(model);
			}

			loader.load(camModel, (gltf) => {
				const newModel = gltf.scene.clone();
				newModel.uuid = data.data.userData._id;
				newModel.position.set(
					data.data.position.x,
					data.data.position.y,
					data.data.position.z
				);
				newModel.rotation.set(
					data.data.rotation.x,
					data.data.rotation.y,
					data.data.rotation.z
				);
				newModel.userData = data.data.userData;

				setCams((prev) => dedupeCams([...prev, newModel]));
				setActiveUsers((prev: any) =>
					dedupeUsers([...prev, data.data.userData])
				);
			});
		});

		socket.on("userDisConnectResponse", (data: any) => {
			if (!groupRef.current) return;
			if (socket.id === data.socketId || organization !== data.organization)
				return;

			setCams((prev) =>
				prev.filter((cam) => cam.uuid !== data.data.userData._id)
			);
			setActiveUsers((prev: any) =>
				prev.filter((user: any) => user._id !== data.data.userData._id)
			);
		});

		socket.on("cameraUpdateResponse", (data: any) => {
			if (
				!groupRef.current ||
				socket.id === data.socketId ||
				organization !== data.organization
			)
				return;

			setModels((prev) => ({
				...prev,
				[data.data.userId]: {
					targetPosition: new THREE.Vector3(
						data.data.position.x,
						data.data.position.y,
						data.data.position.z
					),
					targetRotation: new THREE.Euler(
						data.data.rotation.x,
						data.data.rotation.y,
						data.data.rotation.z
					),
				},
			}));
		});

		return () => {
			socket.off("userConnectResponse");
			socket.off("userDisConnectResponse");
			socket.off("cameraUpdateResponse");
		};
	}, [socket]);

	useFrame(() => {
		if (!groupRef.current) return;
		Object.keys(models).forEach((uuid) => {
			const model = groupRef.current!.getObjectByProperty("uuid", uuid);
			if (!model) return;

			const { targetPosition, targetRotation } = models[uuid];
			model.position.lerp(targetPosition, 0.1);
			model.rotation.x = THREE.MathUtils.lerp(
				model.rotation.x,
				targetRotation.x,
				0.1
			);
			model.rotation.y = THREE.MathUtils.lerp(
				model.rotation.y,
				targetRotation.y,
				0.1
			);
			model.rotation.z = THREE.MathUtils.lerp(
				model.rotation.z,
				targetRotation.z,
				0.1
			);
		});
	});

	useEffect(() => {
		if (!groupRef.current) return;
		const organization = email!.split("@")[1].split(".")[0];

		getActiveUsersData(organization).then((data) => {
			const filteredData = data.cameraDatas.filter(
				(camera: any) => camera.userData.email !== email
			);

			if (filteredData.length > 0) {
				loader.load(camModel, (gltf) => {
					const newCams = filteredData.map((cam: any) => {
						const newModel = gltf.scene.clone();
						newModel.uuid = cam.userData._id;
						newModel.position.set(
							cam.position.x,
							cam.position.y,
							cam.position.z
						);
						newModel.rotation.set(
							cam.rotation.x,
							cam.rotation.y,
							cam.rotation.z
						);
						newModel.userData = cam.userData;
						return newModel;
					});

					const users = filteredData.map((cam: any) => cam.userData);
					setActiveUsers((prev: any) => dedupeUsers([...prev, ...users]));
					setCams((prev) => dedupeCams([...prev, ...newCams]));
				});
			}
		});
	}, []);

	return (
		<group
			ref={groupRef}
			name="Cam-Model-Group"
			visible={activeModule !== "visualization" ? true : false}
		>
			{cams.map((cam, index) => (
				<primitive key={cam.uuid} object={cam}>
					<Html
						as="div"
						center
						zIndexRange={[1, 0]}
						sprite
						style={{
							color: "white",
							textAlign: "center",
							fontFamily: "Arial, sans-serif",
							display: `${activeModule !== "visualization" ? "" : "none"}`,
						}}
						position={[-0.015, 0, 0.7]}
					>
						<CollabUserIcon
							userImage={cam.userData.userImage || ""}
							userName={cam.userData.userName}
							color={getAvatarColor(index, cam.userData.userName)}
						/>
					</Html>
				</primitive>
			))}
		</group>
	);
};

export default CamModelsGroup;
