import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react";
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
import assetManager, {
    cancelOngoingTasks,
} from "../geomentries/assets/assetManager";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
import DeleteFloorItems from "../geomentries/assets/deleteFloorItems";
import loadInitialFloorItems from "../IntialLoad/loadInitialFloorItems";
import addAssetModel from "../geomentries/assets/addAssetModel";
import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import useModuleStore from "../../../store/useModuleStore";
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
import { useEventsStore } from "../../../store/simulation/useEventsStore";

const assetManagerWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", import.meta.url));
const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url));

const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane, }: any) => {
    const state: Types.ThreeState = useThree();
    const { raycaster, controls }: any = state;
    const { renderDistance } = useRenderDistance();
    const { toggleView } = useToggleView();
    const { floorItems, setFloorItems } = useFloorItems();
    const { camMode } = useCamMode();
    const { deleteTool } = useDeleteTool();
    const { setDeletableFloorItem } = useDeletableFloorItem();
    const { transformMode } = useTransformMode();
    const { setSelectedFloorItem } = useSelectedFloorItem();
    const { activeTool } = useActiveTool();
    const { selectedItem, setSelectedItem } = useSelectedItem();
    const { setLoadingProgress } = useLoadingProgress();
    const { activeModule } = useModuleStore();
    const { socket } = useSocketStore();
    const loader = new GLTFLoader();
    const dracoLoader = new DRACOLoader();
    const { addEvent } = useEventsStore();

    dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/");
    loader.setDRACOLoader(dracoLoader);

    useEffect(() => {
        const email = localStorage.getItem("email");
        const organization = email!.split("@")[1].split(".")[0];

        let totalAssets = 0;
        let loadedAssets = 0;

        const updateLoadingProgress = (progress: number) => {
            if (progress < 100) {
                setLoadingProgress(progress);
            } else if (progress === 100) {
                setTimeout(() => {
                    setLoadingProgress(100);
                    setTimeout(() => {
                        setLoadingProgress(0);
                    }, 1500);
                }, 1000);
            }
        };

        getFloorAssets(organization).then((data) => {
            if (data.length > 0) {
                const uniqueItems = (data as Types.FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID));
                totalAssets = uniqueItems.length;
                if (totalAssets === 0) {
                    updateLoadingProgress(100);
                    return;
                }
                gltfLoaderWorker.postMessage({ floorItems: data });
            } else {
                gltfLoaderWorker.postMessage({ floorItems: [] });
                loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
                updateLoadingProgress(100);
            }
        });

        gltfLoaderWorker.onmessage = async (event) => {
            if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
                const blobUrl = URL.createObjectURL(event.data.modelBlob);

                loader.load(blobUrl, (gltf) => {
                    URL.revokeObjectURL(blobUrl);
                    THREE.Cache.remove(blobUrl);
                    THREE.Cache.add(event.data.modelID, gltf);

                    loadedAssets++;
                    const progress = Math.round((loadedAssets / totalAssets) * 100);
                    updateLoadingProgress(progress);

                    if (loadedAssets === totalAssets) {
                        loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
                        updateLoadingProgress(100);
                    }
                });
            }
        };
    }, []);

    useEffect(() => {
        assetManagerWorker.onmessage = async (event) => {
            cancelOngoingTasks(); // Cancel the ongoing process
            await assetManager(event.data, itemsGroup, loader);
        };
    }, [assetManagerWorker]);

    useEffect(() => {
        if (toggleView) return;

        const uuids: string[] = [];
        itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); });
        const cameraPosition = state.camera.position;

        assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, });
    }, [camMode, renderDistance]);

    useEffect(() => {
        const controls: any = state.controls;
        const camera: any = state.camera;

        if (controls) {
            let intervalId: NodeJS.Timeout | null = null;

            const handleChange = () => {
                if (toggleView) return;

                const uuids: string[] = [];
                itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); });
                const cameraPosition = camera.position;

                assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, });
            };

            const startInterval = () => {
                if (!intervalId) {
                    intervalId = setInterval(handleChange, 50);
                }
            };

            const stopInterval = () => {
                handleChange();
                if (intervalId) {
                    clearInterval(intervalId);
                    intervalId = null;
                }
            };

            controls.addEventListener("rest", handleChange);
            controls.addEventListener("rest", stopInterval);
            controls.addEventListener("control", startInterval);
            controls.addEventListener("controlend", stopInterval);

            return () => {
                controls.removeEventListener("rest", handleChange);
                controls.removeEventListener("rest", stopInterval);
                controls.removeEventListener("control", startInterval);
                controls.removeEventListener("controlend", stopInterval);
                if (intervalId) {
                    clearInterval(intervalId);
                }
            };
        }
    }, [state.controls, floorItems, toggleView, renderDistance]);

    useEffect(() => {
        const canvasElement = state.gl.domElement;
        let drag = false;
        let isLeftMouseDown = false;

        const onMouseDown = (evt: any) => {
            if (evt.button === 0) {
                isLeftMouseDown = true;
                drag = false;
            }
        };

        const onMouseMove = () => {
            if (isLeftMouseDown) {
                drag = true;
            }
        };

        const onMouseUp = async (evt: any) => {
            if (controls) {
                (controls as any).enabled = true;
            }
            if (evt.button === 0) {
                isLeftMouseDown = false;
                if (drag) return;

                if (deleteTool) {
                    DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket);
                }
                const Mode = transformMode;

                if (Mode !== null || activeTool === "cursor") {
                    if (!itemsGroup.current) return;
                    let intersects = raycaster.intersectObjects(
                        itemsGroup.current.children,
                        true
                    );
                    if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
                        // let currentObject = intersects[0].object;
                        // while (currentObject) {
                        //     if (currentObject.name === "Scene") {
                        //         break;
                        //     }
                        //     currentObject = currentObject.parent as THREE.Object3D;
                        // }
                        // if (currentObject) {
                        //     AttachedObject.current = currentObject as any;
                        //     setSelectedFloorItem(AttachedObject.current!);
                        // }
                    } else {
                        const target = controls.getTarget(new THREE.Vector3());
                        await controls.setTarget(target.x, 0, target.z, true);
                        setSelectedFloorItem(null);
                    }
                }
            }
        };

        const onDblClick = async (evt: any) => {
            if (evt.button === 0) {
                isLeftMouseDown = false;
                if (drag) return;

                const Mode = transformMode;

                if (Mode !== null || activeTool === "cursor") {
                    if (!itemsGroup.current) return;
                    let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
                    if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
                        let currentObject = intersects[0].object;

                        while (currentObject) {
                            if (currentObject.name === "Scene") {
                                break;
                            }
                            currentObject = currentObject.parent as THREE.Object3D;
                        }
                        if (currentObject) {
                            AttachedObject.current = currentObject as any;
                            // controls.fitToSphere(AttachedObject.current!, true);

                            const bbox = new THREE.Box3().setFromObject(AttachedObject.current);
                            const size = bbox.getSize(new THREE.Vector3());
                            const center = bbox.getCenter(new THREE.Vector3());

                            const front = new THREE.Vector3(0, 0, 1);
                            AttachedObject.current.localToWorld(front);
                            front.sub(AttachedObject.current.position).normalize();

                            const distance = Math.max(size.x, size.y, size.z) * 2;
                            const newPosition = center.clone().addScaledVector(front, distance);

                            controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
                            controls.setTarget(center.x, center.y, center.z, true);
                            controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, });

                            setSelectedFloorItem(AttachedObject.current!);
                        }
                    } else {
                        const target = controls.getTarget(new THREE.Vector3());
                        await controls.setTarget(target.x, 0, target.z, true);
                        setSelectedFloorItem(null);
                    }
                }
            }
        };

        const onDrop = (event: any) => {
            if (!event.dataTransfer?.files[0]) return;

            if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
                addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, addEvent, plane);
            }
        };

        const onDragOver = (event: any) => {
            event.preventDefault();
        };

        if (activeModule === "builder") {
            canvasElement.addEventListener("mousedown", onMouseDown);
            canvasElement.addEventListener("mouseup", onMouseUp);
            canvasElement.addEventListener("mousemove", onMouseMove);
            canvasElement.addEventListener("dblclick", onDblClick);
            canvasElement.addEventListener("drop", onDrop);
            canvasElement.addEventListener("dragover", onDragOver);
        } else {
            if (controls) {
                const target = controls.getTarget(new THREE.Vector3());
                controls.setTarget(target.x, 0, target.z, true);
                setSelectedFloorItem(null);
            }
        }

        return () => {
            canvasElement.removeEventListener("mousedown", onMouseDown);
            canvasElement.removeEventListener("mouseup", onMouseUp);
            canvasElement.removeEventListener("mousemove", onMouseMove);
            canvasElement.removeEventListener("dblclick", onDblClick);
            canvasElement.removeEventListener("drop", onDrop);
            canvasElement.removeEventListener("dragover", onDragOver);
        };
    }, [deleteTool, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);

    useFrame(() => {
        if (controls)
            // assetVisibility(itemsGroup, state.camera.position, renderDistance);
            if (deleteTool && activeModule === "builder") {
                DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
            } else if (!deleteTool) {
                if (hoveredDeletableFloorItem.current) {
                    hoveredDeletableFloorItem.current = undefined;
                    setDeletableFloorItem(null);
                }
            }
    });

    return <group ref={itemsGroup} name="itemsGroup"></group>;
};

export default FloorItemsGroup;
