import * as THREE from "three"
import { useEffect } from 'react'
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
import { useLoadingProgress, useRenameModeStore, 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, RefMesh } from "../../../types/world/worldTypes";
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";
import { useLeftData, useTopData } from "../../../store/visualization/useZone3DWidgetStore";
import { getUserData } from "../../../functions/getUserData";
import { useSceneContext } from "../../scene/sceneContext";
import { useVersionContext } from "../version/versionContext";

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

function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
    const { activeModule } = useModuleStore();
    const { socket } = useSocketStore();
    const { controls, gl, pointer, camera, raycaster, scene } = useThree();
    const { setLoadingProgress } = useLoadingProgress();
    const { assetStore, eventStore } = useSceneContext();
    const { selectedVersionStore } = useVersionContext();
    const { selectedVersion } = selectedVersionStore();
    const { setAssets, addAsset, clearAssets } = assetStore();
    const { addEvent, clearEvents } = eventStore();
    const { setSelectedFloorItem } = useSelectedFloorItem();
    const { selectedItem, setSelectedItem } = useSelectedItem();
    const { projectId } = useParams();
    const { isRenameMode, setIsRenameMode } = useRenameModeStore();
    const { userId, organization } = getUserData();
    const { setTop } = useTopData();
    const { setLeft } = useLeftData();

    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(() => {
        if (!projectId || !selectedVersion) return;

        clearEvents();

        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, selectedVersion?.versionId || '').then((data) => {
            if (data && data.length > 0) {
                const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.assetId === item.assetId));
                totalAssets = uniqueItems.length;
                if (totalAssets === 0) {
                    updateLoadingProgress(100);
                    return;
                }
                gltfLoaderWorker.postMessage({ floorItems: uniqueItems });
            } else {
                gltfLoaderWorker.postMessage({ floorItems: [] });
                updateLoadingProgress(100);
                clearAssets();
            }
        }).catch((err) => {
            console.error(err);
        })

        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, selectedVersion.versionId || '').then((data: FloorItems) => {
                            data.forEach((item) => {
                                if (item.eventData) {
                                    assets.push({
                                        modelUuid: item.modelUuid,
                                        modelName: item.modelName,
                                        assetId: item.assetId,
                                        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",
                                            subType: item.eventData.subType as VehicleEventSchema['subType'] || '',
                                            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,
                                                    paths: {
                                                        initPickup: [],
                                                        pickupDrop: [],
                                                        dropPickup: [],
                                                    },
                                                    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",
                                            subType: item.eventData.subType || '',
                                            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",
                                            subType: item.eventData.subType || '',
                                            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: "Default Material",
                                                    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",
                                            subType: item.eventData.subType || '',
                                            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",
                                            storageCapacity: 10,
                                            storageCount: 10,
                                            materialType: "Default material",
                                            subType: item.eventData.subType || '',
                                            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: "store",
                                                        triggers: []
                                                    }
                                                ]
                                            }
                                        };
                                        addEvent(storageEvent);
                                    } else if (item.eventData.type === 'Human') {
                                        const humanEvent: HumanEventSchema = {
                                            modelUuid: item.modelUuid,
                                            modelName: item.modelName,
                                            position: item.position,
                                            rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                            state: "idle",
                                            type: "human",
                                            subType: item.eventData.subType || '',
                                            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: "worker",
                                                        loadCount: 1,
                                                        manufactureCount: 1,
                                                        loadCapacity: 1,
                                                        processTime: 10,
                                                        triggers: []
                                                    }
                                                ]
                                            }
                                        }
                                        addEvent(humanEvent);
                                    } else if (item.eventData.type === 'Crane') {
                                        const craneEvent: CraneEventSchema = {
                                            modelUuid: item.modelUuid,
                                            modelName: item.modelName,
                                            position: item.position,
                                            rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
                                            state: "idle",
                                            type: "crane",
                                            subType: item.eventData.subType as CraneEventSchema['subType'] || 'pillarJib',
                                            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: "pickAndDrop",
                                                        maxPickUpCount: 1,
                                                        triggers: []
                                                    }
                                                ]
                                            }
                                        }
                                        addEvent(craneEvent);
                                    }
                                } else {
                                    assets.push({
                                        modelUuid: item.modelUuid,
                                        modelName: item.modelName,
                                        assetId: item.assetId,
                                        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);
                    }
                });
            }
        };
    }, [selectedVersion?.versionId]);

    useEffect(() => {
        const canvasElement = gl.domElement;

        const onDrop = (event: DragEvent) => {
            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(scene, raycaster, camera, pointer, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, loader, selectedVersion, projectId, userId);
            }
        };

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

        const onMouseMove = (evt: any) => {
            if (!canvasElement) return;
            const canvasRect = canvasElement.getBoundingClientRect();
            const relativeX = evt.clientX - canvasRect.left;
            const relativeY = evt.clientY - canvasRect.top;
            if (!isRenameMode) {
                setTop(relativeY);
                setLeft(relativeX);
            }
        };
        const onMouseUp = (evt: any) => {
            setIsRenameMode(false);
        }

        if (activeModule === "builder") {
            canvasElement.addEventListener("drop", onDrop);
            canvasElement.addEventListener("dragover", onDragOver);
            canvasElement.addEventListener("mousemove", onMouseMove);
            canvasElement.addEventListener("mouseup", onMouseUp);
        } 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);
            canvasElement.removeEventListener("mousemove", onMouseMove);
            canvasElement.removeEventListener("mouseup", onMouseUp);
        };
    }, [selectedItem, camera, activeModule, controls, isRenameMode]);

    return (
        <Models loader={loader} />
    )
}

export default AssetsGroup;