import * as THREE from "three";
import { useEffect, useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView } from "../../../../store/store";
import { toast } from "react-toastify";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import * as Types from "../../../../types/world/worldTypes";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";

const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
    const { camera, controls, gl, scene, pointer, raycaster } = useThree();
    const { toggleView } = useToggleView();
    const { selectedAssets, setSelectedAssets } = useSelectedAssets();
    const { simulationStates, setSimulationStates } = useSimulationStates();
    const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
    const { floorItems, setFloorItems } = useFloorItems();
    const { socket } = useSocketStore();

    useEffect(() => {
        if (!camera || !scene || toggleView) return;
        const canvasElement = gl.domElement;
        canvasElement.tabIndex = 0;

        let isMoving = false;

        const onPointerDown = () => {
            isMoving = false;
        };

        const onPointerMove = () => {
            isMoving = true;
        };

        const onPointerUp = (event: PointerEvent) => {
            if (!isMoving && duplicatedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
                event.preventDefault();
                addDuplicatedAssets();
            }
        };

        const onKeyDown = (event: KeyboardEvent) => {
            if (event.key.toLowerCase() === "d") {
                event.preventDefault();
                if (event.ctrlKey && event.key.toLowerCase() === "d" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
                    duplicateSelection();
                }
            }
        };

        if (!toggleView) {
            canvasElement.addEventListener("pointerdown", onPointerDown);
            canvasElement.addEventListener("pointermove", onPointerMove);
            canvasElement.addEventListener("pointerup", onPointerUp);
            canvasElement.addEventListener("keydown", onKeyDown);
        }

        return () => {
            canvasElement.removeEventListener("pointerdown", onPointerDown);
            canvasElement.removeEventListener("pointermove", onPointerMove);
            canvasElement.removeEventListener("pointerup", onPointerUp);
            canvasElement.removeEventListener("keydown", onKeyDown);
        };

    }, [camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects]);

    useFrame(() => {
        if (duplicatedObjects.length > 0) {
            const intersectionPoint = new THREE.Vector3();
            raycaster.setFromCamera(pointer, camera);
            const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
            if (point) {
                const position = new THREE.Vector3();
                if (boundingBoxRef.current) {
                    boundingBoxRef.current?.getWorldPosition(position)
                    selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
                } else {
                    const box = new THREE.Box3();
                    duplicatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone()));
                    const center = new THREE.Vector3();
                    box.getCenter(center);
                    selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
                }
            }
        }
    });

    const duplicateSelection = () => {
        if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
            const newClones = selectedAssets.map((asset: any) => {
                const clone = asset.clone();
                clone.position.copy(asset.position);
                return clone;
            });

            selectionGroup.current.add(...newClones);
            setDuplicatedObjects(newClones);

            const intersectionPoint = new THREE.Vector3();
            raycaster.setFromCamera(pointer, camera);
            const point = raycaster.ray.intersectPlane(plane, intersectionPoint);

            if (point) {
                const position = new THREE.Vector3();
                boundingBoxRef.current?.getWorldPosition(position)
                selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
            }
        }
    };

    const addDuplicatedAssets = () => {
        if (duplicatedObjects.length === 0) return;
        duplicatedObjects.forEach(async (obj: THREE.Object3D) => {
            const worldPosition = new THREE.Vector3();
            obj.getWorldPosition(worldPosition);
            obj.position.copy(worldPosition);

            if (itemsGroupRef.current) {

                const newFloorItem: Types.FloorItemType = {
                    modeluuid: obj.uuid,
                    modelname: obj.userData.name,
                    modelfileID: obj.userData.modelId,
                    position: [worldPosition.x, worldPosition.y, worldPosition.z],
                    rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
                    isLocked: false,
                    isVisible: true
                };

                setFloorItems((prevItems: Types.FloorItems) => {
                    const updatedItems = [...(prevItems || []), newFloorItem];
                    localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
                    return updatedItems;
                });

                let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);

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

                if (eventData) {
                    if (eventData.type === 'Conveyor' && eventData) {
                        const createConveyorPoint = (index: number) => {
                            const pointUUID = THREE.MathUtils.generateUUID();
                            const hasActions = (eventData as Types.ConveyorEventsSchema)?.points[index].actions.length > 0;

                            const defaultAction = {
                                uuid: THREE.MathUtils.generateUUID(),
                                name: 'Action 1',
                                type: 'Inherit',
                                material: 'Inherit',
                                delay: 'Inherit',
                                spawnInterval: 'Inherit',
                                isUsed: true
                            };

                            return {
                                uuid: pointUUID,
                                position: (eventData as Types.ConveyorEventsSchema)?.points[index].position,
                                rotation: (eventData as Types.ConveyorEventsSchema)?.points[index].rotation,
                                actions: hasActions
                                    ? (eventData as Types.ConveyorEventsSchema)?.points[index].actions.map(action => ({
                                        ...action,
                                        uuid: THREE.MathUtils.generateUUID()
                                    }))
                                    : [defaultAction],
                                triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers,
                                connections: {
                                    source: { modelUUID: newFloorItem.modeluuid, pointUUID },
                                    targets: []
                                }
                            };
                        };

                        const backendEventData = {
                            type: 'Conveyor',
                            points: [
                                createConveyorPoint(0),  // point1
                                createConveyorPoint(1),  // middlePoint
                                createConveyorPoint(2)   // point2
                            ],
                            speed: (eventData as Types.ConveyorEventsSchema)?.speed
                        };

                        //REST

                        // setFloorItemApi(
                        //     organization,
                        //     obj.uuid,
                        //     obj.userData.name,
                        //     obj.userData.modelId,
                        //     [worldPosition.x, worldPosition.y, worldPosition.z],
                        //     { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
                        //     false,
                        //     true,
                        //     { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }
                        // );

                        //SOCKET

                        const data = {
                            organization,
                            modeluuid: newFloorItem.modeluuid,
                            modelname: newFloorItem.modelname,
                            modelfileID: newFloorItem.modelfileID,
                            position: newFloorItem.position,
                            rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
                            isLocked: false,
                            isVisible: true,
                            eventData: { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed },
                            socketId: socket.id,
                        };

                        const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed };
                        newEventData.modeluuid = newFloorItem.modeluuid;
                        newEventData.modelName = newFloorItem.modelname;
                        newEventData.position = newFloorItem.position;
                        newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];

                        setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
                            ...(prevEvents || []),
                            newEventData as Types.ConveyorEventsSchema
                        ]);

                        socket.emit("v2:model-asset:add", data);

                    } else if (eventData.type === 'Vehicle' && eventData) {
                        const createVehiclePoint = () => {
                            const pointUUID = THREE.MathUtils.generateUUID();
                            const vehiclePoint = (eventData as Types.VehicleEventsSchema)?.points;
                            const hasActions = vehiclePoint?.actions !== undefined;

                            const defaultAction = {
                                uuid: THREE.MathUtils.generateUUID(),
                                name: 'Action 1',
                                type: 'Inherit',
                                start: {},
                                hitCount: 0,
                                end: {},
                                buffer: 0
                            };

                            return {
                                uuid: pointUUID,
                                position: vehiclePoint?.position,
                                actions: hasActions
                                    ? {
                                        ...vehiclePoint.actions,
                                        uuid: THREE.MathUtils.generateUUID()
                                    }
                                    : defaultAction,
                                connections: {
                                    source: { modelUUID: obj.uuid, pointUUID },
                                    targets: []
                                },
                                speed: vehiclePoint?.speed || 2
                            };
                        };

                        const backendEventData = {
                            type: 'Vehicle',
                            points: createVehiclePoint()
                        };

                        // API

                        // setFloorItemApi(
                        //     organization,
                        //     obj.uuid,
                        //     obj.userData.name,
                        //     obj.userData.modelId,
                        //     [worldPosition.x, worldPosition.y, worldPosition.z],
                        //     { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
                        //     false,
                        //     true,
                        //     { type: backendEventData.type, points: backendEventData.points }
                        // );

                        // SOCKET

                        const data = {
                            organization,
                            modeluuid: newFloorItem.modeluuid,
                            modelname: newFloorItem.modelname,
                            modelfileID: newFloorItem.modelfileID,
                            position: newFloorItem.position,
                            rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
                            isLocked: false,
                            isVisible: true,
                            eventData: { type: backendEventData.type, points: backendEventData.points },
                            socketId: socket.id,
                        };

                        const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
                        newEventData.modeluuid = newFloorItem.modeluuid;
                        newEventData.modelName = newFloorItem.modelname;
                        newEventData.position = newFloorItem.position;

                        setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
                            ...(prevEvents || []),
                            newEventData as Types.VehicleEventsSchema
                        ]);

                        socket.emit("v2:model-asset:add", data);

                    } else {

                        //REST

                        // await setFloorItemApi(
                        //     organization,
                        //     obj.uuid,
                        //     obj.userData.name,
                        //     [worldPosition.x, worldPosition.y, worldPosition.z],
                        //     { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
                        //     obj.userData.modelId,
                        //     false,
                        //     true,
                        // );

                        //SOCKET

                        const data = {
                            organization,
                            modeluuid: newFloorItem.modeluuid,
                            modelname: newFloorItem.modelname,
                            modelfileID: newFloorItem.modelfileID,
                            position: newFloorItem.position,
                            rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
                            isLocked: false,
                            isVisible: true,
                            socketId: socket.id,
                        };

                        socket.emit("v2:model-asset:add", data);

                    }
                } else {

                    //REST

                    // await setFloorItemApi(
                    //     organization,
                    //     obj.uuid,
                    //     obj.userData.name,
                    //     [worldPosition.x, worldPosition.y, worldPosition.z],
                    //     { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
                    //     obj.userData.modelId,
                    //     false,
                    //     true,
                    // );

                    //SOCKET

                    const data = {
                        organization,
                        modeluuid: newFloorItem.modeluuid,
                        modelname: newFloorItem.modelname,
                        modelfileID: newFloorItem.modelfileID,
                        position: newFloorItem.position,
                        rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
                        isLocked: false,
                        isVisible: true,
                        socketId: socket.id,
                    };

                    socket.emit("v2:model-asset:add", data);

                }

                obj.userData.modeluuid = newFloorItem.modeluuid;
                itemsGroupRef.current.add(obj);
            }
        });

        toast.success("Object duplicated!");
        clearSelection();
    }

    const clearSelection = () => {
        selectionGroup.current.children = [];
        selectionGroup.current.position.set(0, 0, 0);
        selectionGroup.current.rotation.set(0, 0, 0);
        setMovedObjects([]);
        setpastedObjects([]);
        setDuplicatedObjects([]);
        setRotatedObjects([]);
        setSelectedAssets([]);
    }

    return (
        <></>
    );
};

export default DuplicationControls;