import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import gsap from 'gsap';
import * as THREE from 'three';
import * as CONSTANTS from '../../../types/world/worldConstants';
import { toast } from 'react-toastify';
import * as Types from "../../../types/world/worldTypes";
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator';

async function loadInitialFloorItems(
    itemsGroup: Types.RefGroup,
    setFloorItems: Types.setFloorItemSetState,
    addEvent: (event: EventsSchema) => void,
    renderDistance: number
): Promise<void> {
    if (!itemsGroup.current) return;
    let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
    const email = localStorage.getItem('email');
    const organization = (email!.split("@")[1]).split(".")[0];

    const items = await getFloorAssets(organization);
    localStorage.setItem("FloorItems", JSON.stringify(items));
    await initializeDB();

    if (items.message === "floorItems not found") return;

    if (items) {
        const storedFloorItems: Types.FloorItems = items;
        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);

        let modelsLoaded = 0;
        const modelsToLoad = storedFloorItems.length;

        const camData = await getCamera(organization, localStorage.getItem('userId')!);
        let storedPosition;
        if (camData && camData.position) {
            storedPosition = camData?.position;
        } else {
            storedPosition = new THREE.Vector3(0, 40, 30);
        }
        if (!storedPosition) return;
        const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);

        storedFloorItems.sort((a, b) => {
            const aPosition = new THREE.Vector3(a.position[0], a.position[1], a.position[2]);
            const bPosition = new THREE.Vector3(b.position[0], b.position[1], b.position[2]);
            return cameraPosition.distanceTo(aPosition) - cameraPosition.distanceTo(bPosition);
        });

        for (const item of storedFloorItems) {
            if (!item.modelfileID) return;
            const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]);
            let storedPosition;
            if (localStorage.getItem("cameraPosition")) {
                storedPosition = JSON.parse(localStorage.getItem("cameraPosition")!);
            } else {
                storedPosition = new THREE.Vector3(0, 40, 30);
            }

            const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);

            if (cameraPosition.distanceTo(itemPosition) < renderDistance) {
                await new Promise<void>(async (resolve) => {

                    // Check Three.js Cache
                    const cachedModel = THREE.Cache.get(item.modelfileID!);
                    if (cachedModel) {
                        // console.log(`[Cache] Fetching ${item.modelname}`);
                        processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
                        modelsLoaded++;
                        checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
                        return;
                    }

                    // Check IndexedDB
                    const indexedDBModel = await retrieveGLTF(item.modelfileID!);
                    if (indexedDBModel) {
                        // console.log(`[IndexedDB] Fetching ${item.modelname}`);
                        const blobUrl = URL.createObjectURL(indexedDBModel);
                        loader.load(blobUrl, (gltf) => {
                            URL.revokeObjectURL(blobUrl);
                            THREE.Cache.remove(blobUrl);
                            THREE.Cache.add(item.modelfileID!, gltf);
                            processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
                            modelsLoaded++;
                            checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
                        },
                            undefined,
                            (error) => {
                                toast.error(`[IndexedDB] Error loading ${item.modelname}:`);
                                URL.revokeObjectURL(blobUrl);
                                resolve();
                            }
                        );
                        return;
                    }

                    // Fetch from Backend
                    // console.log(`[Backend] Fetching ${item.modelname}`);
                    const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
                    loader.load(modelUrl, async (gltf) => {
                        const modelBlob = await fetch(modelUrl).then((res) => res.blob());
                        await storeGLTF(item.modelfileID!, modelBlob);
                        THREE.Cache.add(item.modelfileID!, gltf);
                        processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
                        modelsLoaded++;
                        checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
                    },
                        undefined,
                        (error) => {
                            toast.error(`[Backend] Error loading ${item.modelname}:`);
                            resolve();
                        }
                    );
                });
            } else {
                // console.log(`Item ${item.modelname} is not near`);
                setFloorItems((prevItems) => [
                    ...(prevItems || []),
                    {
                        modeluuid: item.modeluuid,
                        modelname: item.modelname,
                        position: item.position,
                        rotation: item.rotation,
                        modelfileID: item.modelfileID,
                        isLocked: item.isLocked,
                        isVisible: item.isVisible,
                    },
                ]);

                modelsLoaded++;
                checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { });
            }
        }

        // Dispose loader after all models
        dracoLoader.dispose();
    }
}


function processLoadedModel(
    gltf: any,
    item: Types.FloorItemType,
    itemsGroup: Types.RefGroup,
    setFloorItems: Types.setFloorItemSetState,
    addEvent: (event: EventsSchema) => void,
) {
    const model = gltf.clone();
    model.uuid = item.modeluuid;
    model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
    model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid };
    model.position.set(...item.position);
    model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z);

    model.traverse((child: any) => {
        if (child.isMesh) {
            // Clone the material to ensure changes are independent
            // child.material = child.material.clone();

            child.castShadow = true;
            child.receiveShadow = true;
        }
    });


    itemsGroup?.current?.add(model);

    setFloorItems((prevItems) => [
        ...(prevItems || []),
        {
            modeluuid: item.modeluuid,
            modelname: item.modelname,
            position: item.position,
            rotation: item.rotation,
            modelfileID: item.modelfileID,
            isLocked: item.isLocked,
            isVisible: item.isVisible,
        },
    ]);

    if (item.modelfileID === "a1ee92554935007b10b3eb05") {
        const data = PointsCalculator(
            'Vehicle',
            gltf.clone(),
            new THREE.Vector3(...model.rotation)
        );

        if (!data || !data.points) return;

        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: THREE.MathUtils.generateUUID(),
                position: [data.points[0].x, data.points[0].y, data.points[0].z],
                rotation: [0, 0, 0],
                action: {
                    actionUuid: THREE.MathUtils.generateUUID(),
                    actionName: "Vehicle Action",
                    actionType: "travel",
                    unLoadDuration: 5,
                    loadCapacity: 10,
                    pickUpPoint: null,
                    unLoadPoint: null,
                    triggers: []
                }
            }
        };
        addEvent(vehicleEvent);
    } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
        const data = PointsCalculator(
            'Conveyor',
            gltf.clone(),
            new THREE.Vector3(...model.rotation)
        );

        if (!data || !data.points) return;

        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: data.points.map((point: THREE.Vector3, index: number) => ({
                uuid: THREE.MathUtils.generateUUID(),
                position: [point.x, point.y, point.z],
                rotation: [0, 0, 0],
                action: {
                    actionUuid: THREE.MathUtils.generateUUID(),
                    actionName: `Action ${index + 1}`,
                    actionType: 'default',
                    material: 'Default material',
                    delay: 0,
                    spawnInterval: 5,
                    spawnCount: 1,
                    triggers: []
                }
            }))
        };
        addEvent(ConveyorEvent);
    } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
        // const data = PointsCalculator(
        //     'Conveyor',
        //     gltf.clone(),
        //     new THREE.Vector3(...model.rotation)
        // );

        // if (!data || !data.points) return;

        // const points: ConveyorPointSchema[] = data.points.map((point: THREE.Vector3, index: number) => {
        //     const actionUuid = THREE.MathUtils.generateUUID();
        //     return {
        //         uuid: THREE.MathUtils.generateUUID(),
        //         position: [point.x, point.y, point.z],
        //         rotation: [0, 0, 0],
        //         action: {
        //             actionUuid,
        //             actionName: `Action ${index}`,
        //             actionType: 'default',
        //             material: 'inherit',
        //             delay: 0,
        //             spawnInterval: 5,
        //             spawnCount: 1,
        //             triggers: []
        //         }
        //     };
        // });

        // points.forEach((point, index) => {
        //     if (index < points.length - 1) {
        //         const nextPoint = points[index + 1];
        //         point.action.triggers.push({
        //             triggerUuid: THREE.MathUtils.generateUUID(),
        //             triggerName: `Trigger 1`,
        //             triggerType: "onComplete",
        //             delay: 0,
        //             triggeredAsset: {
        //                 triggeredModel: {
        //                     modelName: item.modelname,
        //                     modelUuid: item.modeluuid
        //                 },
        //                 triggeredPoint: {
        //                     pointName: `Point ${index + 1}`,
        //                     pointUuid: nextPoint.uuid
        //                 },
        //                 triggeredAction: {
        //                     actionName: nextPoint.action.actionName,
        //                     actionUuid: nextPoint.action.actionUuid
        //                 }
        //             }
        //         });
        //     }
        // });

        // 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
        // };
        // addEvent(ConveyorEvent);
    } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
        const data = PointsCalculator(
            'Conveyor',
            gltf.clone(),
            new THREE.Vector3(...model.rotation)
        );

        if (!data || !data.points) return;

        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: data.points.map((point: THREE.Vector3, index: number) => ({
                uuid: THREE.MathUtils.generateUUID(),
                position: [point.x, point.y, point.z],
                rotation: [0, 0, 0],
                action: {
                    actionUuid: THREE.MathUtils.generateUUID(),
                    actionName: `Action ${index}`,
                    actionType: 'default',
                    material: 'inherit',
                    delay: 0,
                    spawnInterval: 5,
                    spawnCount: 1,
                    triggers: []
                }
            }))
        };
        addEvent(ConveyorEvent);
    } else if (item.modelfileID === "29dee78715ad5b853f5c346d") {
        const data = PointsCalculator(
            'StaticMachine',
            gltf.clone(),
            new THREE.Vector3(...model.rotation)
        );

        if (!data || !data.points) return;

        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: THREE.MathUtils.generateUUID(),
                position: [data.points[0].x, data.points[0].y, data.points[0].z],
                rotation: [0, 0, 0],
                action: {
                    actionUuid: THREE.MathUtils.generateUUID(),
                    actionName: "Process Action",
                    actionType: "process",
                    processTime: 10,
                    swapMaterial: "material-id",
                    triggers: []
                }
            }
        };
        addEvent(machineEvent);
    } else if (item.modelfileID === "52e6681fbb743a890d96c914") {
        const data = PointsCalculator(
            'ArmBot',
            gltf.clone(),
            new THREE.Vector3(...model.rotation)
        );

        if (!data || !data.points) return;

        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: THREE.MathUtils.generateUUID(),
                position: [data.points[0].x, data.points[0].y, data.points[0].z],
                rotation: [0, 0, 0],
                actions: [
                    {
                        actionUuid: THREE.MathUtils.generateUUID(),
                        actionName: "Pick and Place",
                        actionType: "pickAndPlace",
                        process: {
                            startPoint: [0, 0, 0],
                            endPoint: [0, 0, 0]
                        },
                        triggers: []
                    }
                ]
            }
        };
        addEvent(roboticArmEvent);
    }

    gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' });
    gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' });
}

function checkLoadingCompletion(
    modelsLoaded: number,
    modelsToLoad: number,
    dracoLoader: DRACOLoader,
    resolve: () => void
) {
    if (modelsLoaded === modelsToLoad) {
        toast.success("Models Loaded!");
        dracoLoader.dispose();
    }
    resolve();
}

export default loadInitialFloorItems;