import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { toast } from 'react-toastify';
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
import * as CONSTANTS from '../../../../types/world/worldConstants';
import { Socket } from 'socket.io-client';
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';

async function AddWallItems(
    selected: any,
    raycaster: THREE.Raycaster,
    CSGGroup: Types.RefMesh,
    setWallItems: Types.setWallItemSetState,
    socket: Socket<any>,
    projectId?: string
): Promise<void> {
    let intersects = raycaster?.intersectObject(CSGGroup.current!, true);
    const wallRaycastIntersection = intersects?.find((child) => child.object.name.includes("WallRaycastReference"));
    let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;

    if (!wallRaycastIntersection) return;

    const intersectionPoint = wallRaycastIntersection;
    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);

    // Check THREE.js cache first
    const cachedModel = THREE.Cache.get(selected.id);
    if (cachedModel) {
        handleModelLoad(cachedModel);
        return;
    }

    // Check IndexedDB cache
    const cachedModelBlob = await retrieveGLTF(selected.id);
    if (cachedModelBlob) {
        const blobUrl = URL.createObjectURL(cachedModelBlob);
        loader.load(blobUrl, (gltf) => {
            URL.revokeObjectURL(blobUrl);
            THREE.Cache.remove(blobUrl);
            THREE.Cache.add(selected.id, gltf);
            handleModelLoad(gltf);
        });
        return;
    }

    // Load from backend if not in any cache
    loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`, async (gltf) => {
        try {
            const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`).then((res) => res.blob());
            await storeGLTF(selected.id, modelBlob);
            THREE.Cache.add(selected.id, gltf);
            await handleModelLoad(gltf);
        } catch (error) {
            console.error('Failed to cache model:', error);
            handleModelLoad(gltf);
        }
    });

    async function handleModelLoad(gltf: GLTF) {
        const model = gltf.scene.clone();
        model.userData = { wall: intersectionPoint.object.parent };

        model.children[0].children.forEach((child) => {
            if (child.name !== "CSG_REF") {
                child.castShadow = true;
                child.receiveShadow = true;
            }
        });

        const boundingBox = new THREE.Box3().setFromObject(model);
        const size = new THREE.Vector3();
        boundingBox.getSize(size);

        const csgscale = [size.x, size.y, size.z] as [number, number, number];

        const center = new THREE.Vector3();
        boundingBox.getCenter(center);
        const csgposition = [center.x, center.y, center.z] as [number, number, number];

        let positionY = selected.subCategory === 'fixed-move' ? 0 : intersectionPoint.point.y;
        if (positionY === 0) {
            positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height;
        }

        const newWallItem = {
            type: selected.subCategory,
            model: model,
            modelName: selected.name,
            assetId: selected.id,
            scale: [1, 1, 1] as [number, number, number],
            csgscale: csgscale,
            csgposition: csgposition,
            position: [intersectionPoint.point.x, positionY, intersectionPoint.point.z] as [number, number, number],
            quaternion: intersectionPoint.object.quaternion.clone() as Types.QuaternionType
        };

        const email = localStorage.getItem('email');
        const organization = email ? (email.split("@")[1]).split(".")[0] : 'default';
        const userId = localStorage.getItem("userId");

        const data = {
            organization: organization,
            modelUuid: model.uuid,
            modelName: newWallItem.modelName,
            assetId: selected.id,
            type: selected.subCategory,
            csgposition: newWallItem.csgposition,
            csgscale: newWallItem.csgscale,
            position: newWallItem.position,
            quaternion: newWallItem.quaternion,
            scale: newWallItem.scale,
            socketId: socket.id, 
            projectId, 
            userId
        };

        socket.emit('v1:wallItems:set', data);

        setWallItems((prevItems) => {
            const updatedItems = [...prevItems, newWallItem];

            const WallItemsForStorage = updatedItems.map(item => {
                const { model, ...rest } = item;
                return {
                    ...rest,
                    modelUuid: model?.uuid,
                };
            });

            localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage));
            echo.success("Model Added!");
            return updatedItems;
        });
    }
}

export default AddWallItems;