import * as THREE from "three";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { SkeletonUtils } from "three-stdlib";
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
import * as Types from "../../../../../types/world/worldTypes";
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../../functions/getUserData";
import { useSceneContext } from "../../../sceneContext";
import { useVersionContext } from "../../../../builder/version/versionContext";

// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";

const CopyPasteControls3D = () => {
    const { camera, controls, gl, scene, pointer, raycaster } = useThree();
    const { toggleView } = useToggleView();
    const { selectedAssets, setSelectedAssets } = useSelectedAssets();
    const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
    const { socket } = useSocketStore();
    const { assetStore, eventStore, undoRedo3DStore } = useSceneContext();
    const { push3D } = undoRedo3DStore();
    const { addEvent } = eventStore();
    const { projectId } = useParams();
    const { assets, addAsset, updateAsset, removeAsset, getAssetById, copiedObjects, setCopiedObjects, pastedObjects, setPastedObjects, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects } = assetStore();
    const { selectedVersionStore } = useVersionContext();
    const { selectedVersion } = selectedVersionStore();
    const { userId, organization } = getUserData();

    const [isPasting, setIsPasting] = useState(false);
    const [relativePositions, setRelativePositions] = useState<THREE.Vector3[]>([]);
    const [centerOffset, setCenterOffset] = useState<THREE.Vector3 | null>(null);
    const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
    const { contextAction, setContextAction } = useContextActionStore()

    const calculateRelativePositions = useCallback((objects: THREE.Object3D[]) => {
        if (objects.length === 0) return { center: new THREE.Vector3(), relatives: [] };

        const box = new THREE.Box3();
        objects.forEach(obj => box.expandByObject(obj));
        const center = new THREE.Vector3();
        box.getCenter(center);

        const relatives = objects.map(obj => {
            const relativePos = new THREE.Vector3().subVectors(obj.position, center);
            return relativePos;
        });

        return { center, relatives };
    }, []);

    useEffect(() => {
        if (contextAction === "copyAsset") {
            setContextAction(null);
            copySelection()
        } else if (contextAction === "pasteAsset") {
            setContextAction(null);
            pasteCopiedObjects()
        }
    }, [contextAction])

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

        let isPointerMoving = false;

        const onPointerDown = (event: PointerEvent) => {
            isPointerMoving = false;
            if (event.button === 0) mouseButtonsDown.current.left = true;
            if (event.button === 2) mouseButtonsDown.current.right = true;
        };

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

        const onPointerUp = (event: PointerEvent) => {
            if (event.button === 0) mouseButtonsDown.current.left = false;
            if (event.button === 2) mouseButtonsDown.current.right = false;

            if (!isPointerMoving && pastedObjects.length > 0 && event.button === 0) {
                event.preventDefault();
                addPastedObjects();
            }
            if (!isPointerMoving && pastedObjects.length > 0 && event.button === 2) {
                event.preventDefault();
                clearSelection();
                pastedObjects.forEach((obj: THREE.Object3D) => {
                    removeAsset(obj.userData.modelUuid);
                });
            }
        };

        const onKeyDown = (event: KeyboardEvent) => {
            const keyCombination = detectModifierKeys(event);

            if (keyCombination === "Ctrl+C" && movedObjects.length === 0 && rotatedObjects.length === 0) {
                copySelection();
            }
            if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
                pasteCopiedObjects();
            }
            if (keyCombination === "ESCAPE" && pastedObjects.length > 0) {
                event.preventDefault();
                clearSelection();
                pastedObjects.forEach((obj: THREE.Object3D) => {
                    removeAsset(obj.userData.modelUuid);
                });
            }
        };

        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);
        };
    }, [assets, camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, rotatedObjects]);

    useFrame(() => {
        if (!isPasting || pastedObjects.length === 0) return;
        if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) return;

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

        if (hit && centerOffset) {
            pastedObjects.forEach((pastedObject: THREE.Object3D, index: number) => {
                const model = scene.getObjectByProperty("uuid", pastedObject.userData.modelUuid);
                if (!model) return;
                const newPos = new THREE.Vector3().addVectors(hit, relativePositions[index]);
                model.position.set(newPos.x, 0, newPos.z);
            });
        }
    });

    const copySelection = () => {
        if (selectedAssets.length > 0) {
            const newClones = selectedAssets.map((asset: any) => {
                const clone = SkeletonUtils.clone(asset);
                clone.position.copy(asset.position);
                return clone;
            });
            setCopiedObjects(newClones);
            echo.info("Objects copied!");
        }
    };

    const pasteCopiedObjects = () => {
        if (copiedObjects.length > 0 && pastedObjects.length === 0) {
            const { center, relatives } = calculateRelativePositions(copiedObjects);
            setRelativePositions(relatives);

            const newPastedObjects = copiedObjects.map((obj: any) => {
                const clone = SkeletonUtils.clone(obj);
                clone.userData.modelUuid = THREE.MathUtils.generateUUID();
                return clone;
            });

            setPastedObjects(newPastedObjects);

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

            if (hit) {
                const offset = new THREE.Vector3().subVectors(center, hit);
                setCenterOffset(offset);
                setIsPasting(true);

                newPastedObjects.forEach((obj: THREE.Object3D, index: number) => {
                    const initialPos = new THREE.Vector3().addVectors(hit, relatives[index]);
                    const asset: Asset = {
                        modelUuid: obj.userData.modelUuid,
                        modelName: obj.userData.modelName,
                        assetId: obj.userData.assetId,
                        position: initialPos.toArray(),
                        rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
                        isLocked: false,
                        isVisible: true,
                        isCollidable: false,
                        opacity: 0.5,
                    };
                    addAsset(asset);
                });
            }
        }
    };

    const addPastedObjects = () => {
        if (pastedObjects.length === 0) return;

        const undoActions: UndoRedo3DAction[] = [];
        const assetsToCopy: AssetData[] = [];

        pastedObjects.forEach(async (pastedAsset: THREE.Object3D) => {
            if (pastedAsset) {
                const assetUuid = pastedAsset.userData.modelUuid;
                const asset = getAssetById(assetUuid);
                const model = scene.getObjectByProperty("uuid", pastedAsset.userData.modelUuid);
                if (!asset || !model) return;
                const position = new THREE.Vector3().copy(model.position);

                const newFloorItem: Types.FloorItemType = {
                    modelUuid: pastedAsset.userData.modelUuid,
                    modelName: pastedAsset.userData.modelName,
                    assetId: pastedAsset.userData.assetId,
                    position: [position.x, position.y, position.z],
                    rotation: { x: asset.rotation[0], y: asset.rotation[1], z: asset.rotation[2] },
                    isLocked: false,
                    isVisible: true,
                };

                let updatedEventData = null;

                if (pastedAsset.userData.eventData) {
                    updatedEventData = JSON.parse(JSON.stringify(pastedAsset.userData.eventData));
                    updatedEventData.modelUuid = newFloorItem.modelUuid;

                    const eventData: any = {
                        type: pastedAsset.userData.eventData.type,
                        subType: pastedAsset.userData.eventData.subType,
                    };

                    if (pastedAsset.userData.eventData.type === "Conveyor") {
                        const ConveyorEvent: ConveyorEventSchema = {
                            modelUuid: newFloorItem.modelUuid,
                            modelName: newFloorItem.modelName,
                            position: newFloorItem.position,
                            rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
                            state: "idle",
                            type: 'transfer',
                            subType: pastedAsset.userData.eventData.subType || '',
                            speed: 1,
                            points: updatedEventData.points.map((point: any, index: number) => ({
                                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);
                        eventData.points = ConveyorEvent.points.map(point => ({
                            uuid: point.uuid,
                            position: point.position,
                            rotation: point.rotation
                        }));

                    } else if (pastedAsset.userData.eventData.type === "Vehicle") {
                        const vehicleEvent: VehicleEventSchema = {
                            modelUuid: newFloorItem.modelUuid,
                            modelName: newFloorItem.modelName,
                            position: newFloorItem.position,
                            rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
                            state: "idle",
                            type: "vehicle",
                            subType: pastedAsset.userData.eventData.subType || '',
                            speed: 1,
                            point: {
                                uuid: THREE.MathUtils.generateUUID(),
                                position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
                                rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
                                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);
                        eventData.point = {
                            uuid: vehicleEvent.point.uuid,
                            position: vehicleEvent.point.position,
                            rotation: vehicleEvent.point.rotation
                        };

                    } else if (pastedAsset.userData.eventData.type === "ArmBot") {
                        const roboticArmEvent: RoboticArmEventSchema = {
                            modelUuid: newFloorItem.modelUuid,
                            modelName: newFloorItem.modelName,
                            position: newFloorItem.position,
                            rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
                            state: "idle",
                            type: "roboticArm",
                            subType: pastedAsset.userData.eventData.subType || '',
                            speed: 1,
                            point: {
                                uuid: THREE.MathUtils.generateUUID(),
                                position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
                                rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
                                actions: [
                                    {
                                        actionUuid: THREE.MathUtils.generateUUID(),
                                        actionName: "Action 1",
                                        actionType: "pickAndPlace",
                                        process: {
                                            startPoint: null,
                                            endPoint: null
                                        },
                                        triggers: []
                                    }
                                ]
                            }
                        };
                        addEvent(roboticArmEvent);
                        eventData.point = {
                            uuid: roboticArmEvent.point.uuid,
                            position: roboticArmEvent.point.position,
                            rotation: roboticArmEvent.point.rotation
                        };

                    } else if (pastedAsset.userData.eventData.type === "StaticMachine") {
                        const machineEvent: MachineEventSchema = {
                            modelUuid: newFloorItem.modelUuid,
                            modelName: newFloorItem.modelName,
                            position: newFloorItem.position,
                            rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
                            state: "idle",
                            type: "machine",
                            subType: pastedAsset.userData.eventData.subType || '',
                            point: {
                                uuid: THREE.MathUtils.generateUUID(),
                                position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
                                rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
                                action: {
                                    actionUuid: THREE.MathUtils.generateUUID(),
                                    actionName: "Action 1",
                                    actionType: "process",
                                    processTime: 10,
                                    swapMaterial: "Default Material",
                                    triggers: []
                                }
                            }
                        };
                        addEvent(machineEvent);
                        eventData.point = {
                            uuid: machineEvent.point.uuid,
                            position: machineEvent.point.position,
                            rotation: machineEvent.point.rotation
                        };
                    } else if (pastedAsset.userData.eventData.type === "Storage") {
                        const storageEvent: StorageEventSchema = {
                            modelUuid: newFloorItem.modelUuid,
                            modelName: newFloorItem.modelName,
                            position: newFloorItem.position,
                            rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
                            state: "idle",
                            type: "storageUnit",
                            storageCapacity: 10,
                            storageCount: 10,
                            materialType: "Default material",
                            subType: pastedAsset.userData.eventData.subType || '',
                            point: {
                                uuid: THREE.MathUtils.generateUUID(),
                                position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
                                rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
                                actions: [
                                    {
                                        actionUuid: THREE.MathUtils.generateUUID(),
                                        actionName: "Action 1",
                                        actionType: "store",
                                        triggers: []
                                    }
                                ]
                            }
                        }
                        addEvent(storageEvent);
                        eventData.point = {
                            uuid: storageEvent.point.uuid,
                            position: storageEvent.point.position,
                            rotation: storageEvent.point.rotation
                        };
                    } else if (pastedAsset.userData.eventData.type === "Human") {
                        const humanEvent: HumanEventSchema = {
                            modelUuid: newFloorItem.modelUuid,
                            modelName: newFloorItem.modelName,
                            position: newFloorItem.position,
                            rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
                            state: "idle",
                            type: "human",
                            subType: pastedAsset.userData.eventData.subType || '',
                            speed: 1,
                            point: {
                                uuid: THREE.MathUtils.generateUUID(),
                                position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
                                rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
                                actions: [
                                    {
                                        actionUuid: THREE.MathUtils.generateUUID(),
                                        actionName: "Action 1",
                                        actionType: "worker",
                                        loadCapacity: 1,
                                        assemblyCount: 1,
                                        assemblyCondition: {
                                            conditionType: 'material',
                                            materialType: "Default material"
                                        },
                                        manufactureCount: 1,
                                        loadCount: 1,
                                        processTime: 10,
                                        triggers: []
                                    }
                                ]
                            }
                        }
                        addEvent(humanEvent);
                        eventData.point = {
                            uuid: humanEvent.point.uuid,
                            position: humanEvent.point.position,
                            rotation: humanEvent.point.rotation
                        };
                    } else if (pastedAsset.userData.eventData.type === "Crane") {
                        const craneEvent: CraneEventSchema = {
                            modelUuid: newFloorItem.modelUuid,
                            modelName: newFloorItem.modelName,
                            position: newFloorItem.position,
                            rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z],
                            state: "idle",
                            type: "crane",
                            subType: pastedAsset.userData.eventData.subType || '',
                            point: {
                                uuid: THREE.MathUtils.generateUUID(),
                                position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]],
                                rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]],
                                actions: [
                                    {
                                        actionUuid: THREE.MathUtils.generateUUID(),
                                        actionName: "Action 1",
                                        actionType: "pickAndDrop",
                                        maxPickUpCount: 1,
                                        triggers: []
                                    }
                                ]
                            }
                        }
                        addEvent(craneEvent);
                        eventData.point = {
                            uuid: craneEvent.point.uuid,
                            position: craneEvent.point.position,
                            rotation: craneEvent.point.rotation
                        };
                    }

                    newFloorItem.eventData = eventData;

                    const data = {
                        organization,
                        modelUuid: newFloorItem.modelUuid,
                        modelName: newFloorItem.modelName,
                        assetId: newFloorItem.assetId,
                        position: [position.x, 0, position.z],
                        rotation: { x: pastedAsset.rotation.x, y: pastedAsset.rotation.y, z: pastedAsset.rotation.z },
                        isLocked: false,
                        isVisible: true,
                        socketId: socket.id,
                        eventData: eventData,
                        versionId: selectedVersion?.versionId || '',
                        userId,
                        projectId
                    };

                    //REST

                    // await setAssetsApi(data);

                    //SOCKET

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

                    const asset: Asset = {
                        modelUuid: data.modelUuid,
                        modelName: data.modelName,
                        assetId: data.assetId,
                        position: [position.x, 0, position.z],
                        rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
                        isLocked: data.isLocked,
                        isCollidable: false,
                        isVisible: data.isVisible,
                        opacity: 1,
                        eventData: data.eventData
                    };

                    updateAsset(asset.modelUuid, asset);
                } else {
                    const data = {
                        organization,
                        modelUuid: newFloorItem.modelUuid,
                        modelName: newFloorItem.modelName,
                        assetId: newFloorItem.assetId,
                        position: [position.x, 0, position.z],
                        rotation: { x: pastedAsset.rotation.x, y: pastedAsset.rotation.y, z: pastedAsset.rotation.z },
                        isLocked: false,
                        isVisible: true,
                        socketId: socket.id,
                        versionId: selectedVersion?.versionId || '',
                        projectId,
                        userId
                    };

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

                    const asset: Asset = {
                        modelUuid: data.modelUuid,
                        modelName: data.modelName,
                        assetId: data.assetId,
                        position: [position.x, 0, position.z],
                        rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
                        isLocked: data.isLocked,
                        isCollidable: false,
                        isVisible: data.isVisible,
                        opacity: 1,
                    };

                    updateAsset(asset.modelUuid, asset);
                }

                assetsToCopy.push({
                    type: "Asset",
                    assetData: {
                        modelUuid: newFloorItem.modelUuid,
                        modelName: newFloorItem.modelName,
                        assetId: newFloorItem.assetId,
                        position: [position.x, position.y, position.z],
                        rotation: [pastedAsset.rotation.x, pastedAsset.rotation.y, pastedAsset.rotation.z],
                        isLocked: false,
                        isVisible: true,
                        isCollidable: false,
                        opacity: 1,
                        eventData: newFloorItem.eventData || undefined
                    },
                    timeStap: new Date().toISOString()
                });
            }
        });

        if (assetsToCopy.length === 1) {
            undoActions.push({
                module: "builder",
                actionType: "Asset-Copied",
                asset: assetsToCopy[0]
            });
        } else {
            undoActions.push({
                module: "builder",
                actionType: "Assets-Copied",
                assets: assetsToCopy
            });
        }

        push3D({
            type: 'Scene',
            actions: undoActions
        });

        echo.success("Object added!");
        clearSelection();
    };

    const clearSelection = () => {
        setMovedObjects([]);
        setPastedObjects([]);
        setDuplicatedObjects([]);
        setRotatedObjects([]);
        setSelectedAssets([]);
        setIsPasting(false);
        setCenterOffset(null);
    };

    return null;
};

export default CopyPasteControls3D;