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

function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) {
    const { camera, controls, gl, scene, pointer, raycaster } = useThree();
    const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);

    const { toggleView } = useToggleView();
    const { selectedAssets, setSelectedAssets } = useSelectedAssets();
    const { simulationStates, setSimulationStates } = useSimulationStates();
    const { floorItems, setFloorItems } = useFloorItems();
    const { socket } = useSocketStore();
    const itemsData = useRef<Types.FloorItems>([]);

    const prevPointerPosition = useRef<THREE.Vector2 | null>(null);

    useEffect(() => {
        if (!camera || !scene || toggleView || !itemsGroupRef.current) 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 && rotatedObjects.length > 0 && event.button === 0) {
                event.preventDefault();
                placeRotatedAssets();
            }
            if (!isMoving && rotatedObjects.length > 0 && event.button === 2) {
                event.preventDefault();

                clearSelection();
                rotatedObjects.forEach((asset: any) => {
                    if (itemsGroupRef.current) {
                        itemsGroupRef.current.attach(asset);
                    }
                });

                setFloorItems([...floorItems, ...itemsData.current]);

                setRotatedObjects([]);
                itemsData.current = [];
            }
        };

        const onKeyDown = (event: KeyboardEvent) => {
            if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return;
            if (event.key.toLowerCase() === "r") {
                if (selectedAssets.length > 0) {
                    rotateAssets();
                    itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
                }
            }
            if (event.key.toLowerCase() === "escape") {
                event.preventDefault();

                clearSelection();
                rotatedObjects.forEach((asset: any) => {
                    if (itemsGroupRef.current) {
                        itemsGroupRef.current.attach(asset);
                    }
                });

                setFloorItems([...floorItems, ...itemsData.current]);

                setRotatedObjects([]);
                itemsData.current = [];
            }
        };

        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, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]);

    useFrame(() => {
        if (rotatedObjects.length > 0) {
            const intersectionPoint = new THREE.Vector3();
            raycaster.setFromCamera(pointer, camera);
            const point = raycaster.ray.intersectPlane(plane, intersectionPoint);

            if (point && prevPointerPosition.current) {
                const box = new THREE.Box3();
                rotatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
                const center = new THREE.Vector3();
                box.getCenter(center);

                const delta = new THREE.Vector3().subVectors(point, center);
                const prevPointerPosition3D = new THREE.Vector3(prevPointerPosition.current.x, 0, prevPointerPosition.current.y);

                const angle = Math.atan2(delta.z, delta.x) - Math.atan2(prevPointerPosition3D.z - center.z, prevPointerPosition3D.x - center.x);

                selectionGroup.current.rotation.y += -angle;

                selectionGroup.current.position.sub(center);
                selectionGroup.current.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), -angle);
                selectionGroup.current.position.add(center);

                prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
            }
        }
    });

    const rotateAssets = () => {
        const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
        setFloorItems(updatedItems);

        const box = new THREE.Box3();
        selectedAssets.forEach((asset: any) => box.expandByObject(asset));
        const center = new THREE.Vector3();
        box.getCenter(center);

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

        if (point) {
            prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
        }

        selectedAssets.forEach((asset: any) => {
            selectionGroup.current.attach(asset);
        });

        setRotatedObjects(selectedAssets);
    };

    const placeRotatedAssets = () => {
        if (rotatedObjects.length === 0) return;

        rotatedObjects.forEach(async (obj: THREE.Object3D) => {
            const worldPosition = new THREE.Vector3();
            const worldQuaternion = new THREE.Quaternion();

            obj.getWorldPosition(worldPosition);
            obj.getWorldQuaternion(worldQuaternion);

            selectionGroup.current.remove(obj);

            obj.position.copy(worldPosition);
            obj.quaternion.copy(worldQuaternion);


            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 backendEventData = {
                            type: 'Conveyor',
                            points: eventData.points,
                            speed: (eventData as Types.ConveyorEventsSchema)?.speed
                        };

                        // REST

                        // await 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)[]) => {
                            const updatedEvents = (prevEvents || []).map(event =>
                                event.modeluuid === newFloorItem.modeluuid
                                    ? { ...event, ...newEventData }
                                    : event
                            );
                            return updatedEvents;
                        });

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

                    } else if (eventData.type === 'Vehicle' && eventData) {

                        const backendEventData = {
                            type: 'Vehicle',
                            points: eventData.points
                        };

                        // REST

                        // await 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;
                        newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];

                        setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
                            const updatedEvents = (prevEvents || []).map(event =>
                                event.modeluuid === newFloorItem.modeluuid
                                    ? { ...event, ...newEventData }
                                    : event
                            );
                            return updatedEvents;
                        });

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

                    } else if (eventData.type === 'StaticMachine' && eventData) {

                        const backendEventData = {
                            type: 'StaticMachine',
                            points: eventData.points,
                        };

                        // REST

                        // await 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;
                        newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];

                        setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
                            const updatedEvents = (prevEvents || []).map(event =>
                                event.modeluuid === newFloorItem.modeluuid
                                    ? { ...event, ...newEventData }
                                    : event
                            );
                            return updatedEvents;
                        });

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

                    } else if (eventData.type === 'ArmBot' && eventData) {

                        const backendEventData = {
                            type: 'ArmBot',
                            points: eventData.points,
                        };

                        // REST

                        // await 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;
                        newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];

                        setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
                            const updatedEvents = (prevEvents || []).map(event =>
                                event.modeluuid === newFloorItem.modeluuid
                                    ? { ...event, ...newEventData }
                                    : event
                            );
                            return updatedEvents;
                        });

                        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);

                }

                itemsGroupRef.current.add(obj);
            }
        });
        toast.success("Object rotated!");

        itemsData.current = [];
        clearSelection();
    }

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

    return null; // No need to return anything, as this component is used for its side effects
}

export default RotateControls