import * as THREE from "three"
import { useEffect } from 'react'
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
import { useLoadingProgress, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { FloorItems, RefGroup, RefMesh } from "../../../types/world/worldTypes";
import { useAssetsStore } from "../../../store/builder/useAssetStore";
import { useEventsStore } from "../../../store/simulation/useEventsStore";
import Models from "./models/models";
import useModuleStore from "../../../store/useModuleStore";
import { useThree } from "@react-three/fiber";
import { CameraControls } from "@react-three/drei";
import addAssetModel from "./functions/addAssetModel";
import { useParams } from "react-router-dom";

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

function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, readonly plane: RefMesh }) {
    const { activeModule } = useModuleStore();
    const { socket } = useSocketStore();
    const { controls, gl, pointer, camera, raycaster } = useThree();
    const { setLoadingProgress } = useLoadingProgress();
    const { setAssets, addAsset } = useAssetsStore();
    const { addEvent } = useEventsStore();
    const { setSelectedFloorItem } = useSelectedFloorItem();
    const { selectedItem, setSelectedItem } = useSelectedItem();
    const { projectId } = useParams();
    const email = localStorage.getItem("email");
    const organization = email!.split("@")[1].split(".")[0];
    const userId = localStorage.getItem("userId")!;

    const loader = new GLTFLoader();
    const dracoLoader = new DRACOLoader();

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

    useEffect(() => {

        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, projectId).then((data) => {
            if (data.length > 0) {
                const uniqueItems = (data as 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: uniqueItems });
            } else {
                gltfLoaderWorker.postMessage({ floorItems: [] });
                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) {
                        const assets: Asset[] = [];
                        getFloorAssets(organization, projectId).then((data: FloorItems) => {
                            data.forEach((item) => {
                                if (item.eventData) {
                                    assets.push({
                                        modelUuid: item.modelUuid,
                                        modelName: item.modelName,
                                        assetId: item.modelfileID,
                                        position: item.position,
                                        rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                        isLocked: item.isLocked,
                                        isCollidable: false,
                                        isVisible: item.isVisible,
                                        opacity: 1,
                                        eventData: item.eventData
                                    })

                                    if (item.eventData.type === "Vehicle") {
                                        const vehicleEvent: VehicleEventSchema = {
                                            modelUuid: item.modelUuid,
                                            modelName: item.modelName,
                                            position: item.position,
                                            rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                            state: "idle",
                                            type: "vehicle",
                                            speed: 1,
                                            point: {
                                                uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
                                                position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
                                                rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
                                                action: {
                                                    actionUuid: THREE.MathUtils.generateUUID(),
                                                    actionName: "Action 1",
                                                    actionType: "travel",
                                                    unLoadDuration: 5,
                                                    loadCapacity: 1,
                                                    steeringAngle: 0,
                                                    pickUpPoint: null,
                                                    unLoadPoint: null,
                                                    triggers: []
                                                }
                                            }
                                        };
                                        addEvent(vehicleEvent);
                                    } else if (item.eventData.type === "Conveyor") {
                                        const ConveyorEvent: ConveyorEventSchema = {
                                            modelUuid: item.modelUuid,
                                            modelName: item.modelName,
                                            position: item.position,
                                            rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                            state: "idle",
                                            type: "transfer",
                                            speed: 1,
                                            points: item.eventData.points?.map((point: any, index: number) => ({
                                                uuid: point.uuid || THREE.MathUtils.generateUUID(),
                                                position: [point.position[0], point.position[1], point.position[2]],
                                                rotation: [point.rotation[0], point.rotation[1], point.rotation[2]],
                                                action: {
                                                    actionUuid: THREE.MathUtils.generateUUID(),
                                                    actionName: `Action 1`,
                                                    actionType: 'default',
                                                    material: 'Default material',
                                                    delay: 0,
                                                    spawnInterval: 5,
                                                    spawnCount: 1,
                                                    triggers: []
                                                }
                                            })) || [],
                                        };
                                        addEvent(ConveyorEvent);
                                    } else if (item.eventData.type === "StaticMachine") {
                                        const machineEvent: MachineEventSchema = {
                                            modelUuid: item.modelUuid,
                                            modelName: item.modelName,
                                            position: item.position,
                                            rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                            state: "idle",
                                            type: "machine",
                                            point: {
                                                uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
                                                position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
                                                rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
                                                action: {
                                                    actionUuid: THREE.MathUtils.generateUUID(),
                                                    actionName: "Action 1",
                                                    actionType: "process",
                                                    processTime: 10,
                                                    swapMaterial: "material-id",
                                                    triggers: []
                                                }
                                            }
                                        };
                                        addEvent(machineEvent);
                                    } else if (item.eventData.type === "ArmBot") {
                                        const roboticArmEvent: RoboticArmEventSchema = {
                                            modelUuid: item.modelUuid,
                                            modelName: item.modelName,
                                            position: item.position,
                                            rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                            state: "idle",
                                            type: "roboticArm",
                                            speed: 1,
                                            point: {
                                                uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
                                                position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
                                                rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
                                                actions: [
                                                    {
                                                        actionUuid: THREE.MathUtils.generateUUID(),
                                                        actionName: "Action 1",
                                                        actionType: "pickAndPlace",
                                                        process: {
                                                            startPoint: null,
                                                            endPoint: null
                                                        },
                                                        triggers: []
                                                    }
                                                ]
                                            }
                                        };
                                        addEvent(roboticArmEvent);
                                    } else if (item.eventData.type === 'Storage') {
                                        const storageEvent: StorageEventSchema = {
                                            modelUuid: item.modelUuid,
                                            modelName: item.modelName,
                                            position: item.position,
                                            rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle",
                                            type: "storageUnit",
                                            point: {
                                                uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
                                                position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
                                                rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
                                                action: {
                                                    actionUuid: THREE.MathUtils.generateUUID(),
                                                    actionName: "Action 1",
                                                    actionType: "store",
                                                    storageCapacity: 10,
                                                    triggers: []
                                                }
                                            }
                                        };
                                        addEvent(storageEvent);
                                    }
                                } else {
                                    assets.push({
                                        modelUuid: item.modelUuid,
                                        modelName: item.modelName,
                                        assetId: item.modelfileID,
                                        position: item.position,
                                        rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                        isLocked: item.isLocked,
                                        isCollidable: false,
                                        isVisible: item.isVisible,
                                        opacity: 1,
                                    })
                                }
                            })
                            setAssets(assets);
                        })
                        updateLoadingProgress(100);
                    }
                });
            }
        };
    }, []);

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

            if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') {

                pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
                pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;

                addAssetModel(raycaster, camera, pointer, floorGroup, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, projectId, userId);
            }
        };

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


        if (activeModule === "builder") {
            canvasElement.addEventListener("drop", onDrop);
            canvasElement.addEventListener("dragover", onDragOver);
        } else {
            if ((controls as CameraControls)) {
                const target = (controls as CameraControls).getTarget(new THREE.Vector3());
                (controls as CameraControls).setTarget(target.x, 0, target.z, true);
                setSelectedFloorItem(null);
            }
        }

        return () => {
            canvasElement.removeEventListener("drop", onDrop);
            canvasElement.removeEventListener("dragover", onDragOver);
        };
    }, [selectedItem, camera, pointer, activeModule, controls]);

    return (
        <Models />
    )
}

export default AssetsGroup;