import * as THREE from "three";
import { useParams } from "react-router-dom";
import { useThree } from "@react-three/fiber";
import { TransformControls } from "@react-three/drei";
import { useRef, useEffect, useState, useCallback } from "react";
import { useToolMode } from "../../../../store/builder/store";
import { useSocketStore } from "../../../../store/socket/useSocketStore";
import { useSceneContext } from "../../sceneContext";
import useAssetResponseHandler from "../../../collaboration/responseHandler/useAssetResponseHandler";

import { getUserData } from "../../../../functions/getUserData";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
import { updateEventToBackend } from "../../../../components/layout/sidebarRight/properties/eventProperties/functions/handleUpdateEventToBackend";

function TransformControls3D() {
    const { toolMode } = useToolMode();
    const { camera, scene, gl } = useThree();
    const transformControlsRef = useRef<any>(null);
    const [visible, setVisible] = useState(false);
    const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
    const { builderSocket, simulationSocket } = useSocketStore();
    const { assetStore, eventStore, productStore, undoRedo3DStore, versionStore } = useSceneContext();
    const { selectedProduct, peekUpdateEvent, updateEvent, getEventByModelUuid } = productStore();
    const { push3D } = undoRedo3DStore();
    const { getAssetById, selectedAssets, setSelectedAssets, assets } = assetStore();
    const { updateAssetInScene } = useAssetResponseHandler();
    const { userId, organization } = getUserData();
    const { selectedVersion } = versionStore();
    const { projectId } = useParams();
    const pivotPointRef = useRef<THREE.Vector3>(new THREE.Vector3());
    const initialPositionsRef = useRef<Map<string, THREE.Vector3>>(new Map());
    const initialRotationsRef = useRef<Map<string, THREE.Euler>>(new Map());
    const initialPivotPositionRef = useRef<THREE.Vector3>(new THREE.Vector3());
    const initialQuaternionsRef = useRef<Map<string, THREE.Quaternion>>(new Map());
    const tempObjectRef = useRef<THREE.Object3D>(new THREE.Object3D());

    const updateBackend = (eventData: EventsSchema) => {
        updateEventToBackend({
            productName: selectedProduct.productName,
            productUuid: selectedProduct.productUuid,
            projectId: projectId || "",
            eventData,
            simulationSocket,
            selectedVersion,
            updateEvent,
        });
    };

    const recalcGizmo = useCallback(() => {
        if (selectedAssets.length === 0) return;

        const bbox = new THREE.Box3();
        selectedAssets.forEach((obj) => bbox.expandByObject(obj));
        bbox.getCenter(pivotPointRef.current);
        initialPivotPositionRef.current.copy(pivotPointRef.current);

        tempObjectRef.current.position.copy(pivotPointRef.current);
        tempObjectRef.current.rotation.set(0, 0, 0);
        tempObjectRef.current.scale.set(1, 1, 1);

        initialPositionsRef.current.clear();
        initialRotationsRef.current.clear();
        initialQuaternionsRef.current.clear();

        selectedAssets.forEach((obj) => {
            initialPositionsRef.current.set(obj.uuid, obj.position.clone());
            initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
            initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
        });
    }, [selectedAssets, assets]);

    useEffect(() => {
        recalcGizmo();
    }, [assets]);

    const handleTransformationComplete = useCallback(() => {
        if (selectedAssets.length === 0) return;

        const updatedAssets = [...selectedAssets];
        setSelectedAssets(updatedAssets);

        selectedAssets.forEach((obj) => {
            const asset = getAssetById(obj.uuid);
            if (!asset) return;

            if (asset.eventData) {
                const productData = getEventByModelUuid(selectedProduct.productUuid, asset.modelUuid);

                if (productData) {
                    const event = peekUpdateEvent(selectedProduct.productUuid, asset.modelUuid, {
                        position: [obj.position.x, obj.position.y, obj.position.z],
                        rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
                    });

                    if (event) {
                        updateBackend(event);
                    }
                }
            }

            if (!builderSocket?.connected) {
                // API

                setAssetsApi({
                    modelUuid: asset.modelUuid,
                    modelName: asset.modelName,
                    assetId: asset.assetId,
                    position: [obj.position.x, obj.position.y, obj.position.z],
                    rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
                    scale: asset.scale,
                    isCollidable: asset.isCollidable,
                    opacity: asset.opacity,
                    isLocked: asset.isLocked,
                    isVisible: asset.isVisible,
                    versionId: selectedVersion?.versionId || "",
                    projectId: projectId || "",
                })
                    .then((data) => {
                        if (!data.message || !data.data) {
                            echo.error(`Error moving asset: ${asset.modelUuid}`);

                            return;
                        }
                        if (data.message === "Model updated successfully" && data.data) {
                            const model: Asset = {
                                modelUuid: data.data.modelUuid,
                                modelName: data.data.modelName,
                                assetId: data.data.assetId,
                                position: data.data.position,
                                rotation: data.data.rotation,
                                scale: data.data.scale,
                                isLocked: data.data.isLocked,
                                isVisible: data.data.isVisible,
                                isCollidable: data.data.isCollidable,
                                opacity: data.data.opacity,
                                ...(data.data.eventData ? { eventData: data.data.eventData } : {}),
                            };

                            updateAssetInScene(model, () => {
                                echo.info(`Moved asset: ${model.modelName}`);
                            });
                        } else {
                            echo.error(`Error moving asset: ${asset.modelUuid}`);
                        }
                    })
                    .catch(() => {
                        echo.error(`Error moving asset: ${asset.modelUuid}`);
                    });
            } else {
                // SOCKET

                const data = {
                    organization,
                    modelUuid: asset.modelUuid,
                    modelName: asset.modelName,
                    assetId: asset.assetId,
                    position: [obj.position.x, obj.position.y, obj.position.z],
                    rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
                    isCollidable: asset.isCollidable,
                    opacity: asset.opacity,
                    isLocked: asset.isLocked,
                    isVisible: asset.isVisible,
                    socketId: builderSocket.id,
                    versionId: selectedVersion?.versionId || "",
                    userId,
                    projectId,
                };

                builderSocket.emit("v1:model-asset:add", data);
            }
        });
    }, [selectedAssets, setSelectedAssets, getAssetById, eventStore, productStore, updateBackend, projectId, organization, builderSocket, selectedVersion, userId, push3D]);

    useEffect(() => {
        const temp = tempObjectRef.current;
        scene.add(temp);
        return () => {
            scene.remove(temp);
        };
    }, [scene]);

    useEffect(() => {
        if (selectedAssets.length === 0) {
            setVisible(false);
            return;
        }

        const bbox = new THREE.Box3();
        selectedAssets.forEach((obj) => bbox.expandByObject(obj));
        bbox.getCenter(pivotPointRef.current);
        initialPivotPositionRef.current.copy(pivotPointRef.current);

        tempObjectRef.current.position.copy(pivotPointRef.current);
        tempObjectRef.current.rotation.set(0, 0, 0);
        tempObjectRef.current.scale.set(1, 1, 1);

        initialPositionsRef.current.clear();
        initialRotationsRef.current.clear();
        initialQuaternionsRef.current.clear();
        selectedAssets.forEach((obj) => {
            initialPositionsRef.current.set(obj.uuid, obj.position.clone());
            initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
            initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
        });

        setVisible(true);
    }, [selectedAssets]);

    useEffect(() => {
        const controls = transformControlsRef.current;
        if (!controls || !visible) return;
        controls.setSize(0.6);

        controls.attach(tempObjectRef.current);
        controls.setMode(toolMode === "Move-Asset" ? "translate" : "rotate");

        const onObjectChange = () => {
            const temp = tempObjectRef.current;
            if (!temp) return;

            selectedAssets.forEach((obj) => {
                const initialPos = initialPositionsRef.current.get(obj.uuid);
                const initialQuat = initialQuaternionsRef.current.get(obj.uuid);
                if (!initialPos || !initialQuat) return;

                if (controls.mode === "translate") {
                    const delta = new THREE.Vector3().copy(temp.position).sub(initialPivotPositionRef.current);
                    obj.position.copy(initialPos).add(delta);
                } else if (controls.mode === "rotate") {
                    const deltaQuat = temp.quaternion.clone();
                    const relPos = initialPos.clone().sub(initialPivotPositionRef.current);
                    relPos.applyQuaternion(deltaQuat);
                    obj.position.copy(initialPivotPositionRef.current).add(relPos);
                    obj.quaternion.copy(deltaQuat).multiply(initialQuat);
                }
            });
        };

        const onMouseUp = () => {
            const assetsToUpdate: AssetData[] = [];
            const undoActions: UndoRedo3DAction[] = [];

            selectedAssets.forEach((obj) => {
                const asset = getAssetById(obj.uuid);
                if (!asset) return;

                const initialPos = initialPositionsRef.current.get(obj.uuid);
                const initialRot = initialRotationsRef.current.get(obj.uuid);
                if (!initialPos || !initialRot) return;

                const assetData: Asset = {
                    ...asset,
                    position: [initialPos.x, initialPos.y, initialPos.z],
                    rotation: [initialRot.x, initialRot.y, initialRot.z],
                };

                const newData: Asset = {
                    ...asset,
                    position: [obj.position.x, obj.position.y, obj.position.z],
                    rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
                };

                assetsToUpdate.push({
                    type: "Asset",
                    assetData,
                    newData,
                    timeStap: new Date().toISOString(),
                });
            });

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

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

            selectedAssets.forEach((obj) => {
                initialPositionsRef.current.set(obj.uuid, obj.position.clone());
                initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
                initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
            });

            if (selectedAssets.length > 0) {
                const bbox = new THREE.Box3();
                selectedAssets.forEach((obj) => bbox.expandByObject(obj));
                bbox.getCenter(pivotPointRef.current);
                initialPivotPositionRef.current.copy(pivotPointRef.current);

                tempObjectRef.current.position.copy(pivotPointRef.current);
                tempObjectRef.current.rotation.set(0, 0, 0);
            }

            recalcGizmo();
            handleTransformationComplete();
        };

        controls.addEventListener("objectChange", onObjectChange);
        controls.addEventListener("mouseUp", onMouseUp);

        return () => {
            controls.removeEventListener("objectChange", onObjectChange);
            controls.removeEventListener("mouseUp", onMouseUp);
            controls.detach();
        };
    }, [selectedAssets, toolMode, visible, handleTransformationComplete, selectedProduct]);

    useEffect(() => {
        const canvasElement = gl.domElement;

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

            if (keyCombination !== keyEvent) {
                if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
                    setKeyEvent(keyCombination);
                } else {
                    setKeyEvent("");
                }
            }
        };

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

            if (keyCombination === "") {
                setKeyEvent("");
            } else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
                setKeyEvent(keyCombination);
            }
        };

        if (visible) {
            canvasElement.addEventListener("keydown", handleKeyDown);
            canvasElement.addEventListener("keyup", onKeyUp);
        }

        return () => {
            canvasElement.removeEventListener("keydown", handleKeyDown);
            canvasElement.removeEventListener("keyup", onKeyUp);
        };
    }, [gl, visible, keyEvent]);

    if (!visible || (toolMode !== "Move-Asset" && toolMode !== "Rotate-Asset")) {
        return null;
    }

    return (
        <TransformControls
            space="world"
            translationSnap={keyEvent === "Ctrl" ? 0.5 : keyEvent === "Ctrl+Shift" ? 0.1 : 0}
            rotationSnap={keyEvent === "Ctrl" ? THREE.MathUtils.degToRad(15) : keyEvent === "Ctrl+Shift" ? THREE.MathUtils.degToRad(5) : 0}
            ref={transformControlsRef}
            camera={camera}
            showX
            showY
            showZ
        />
    );
}

export default TransformControls3D;
