import * as THREE from 'three';
import { CameraControls } from '@react-three/drei';
import { ThreeEvent, useThree } from '@react-three/fiber';
import { useCallback, useEffect, useRef } from 'react';

import { useActiveTool, useDeletableFloorItem, useSelectedFloorItem, useToggleView, useZoneAssetId } from '../../../../../../store/builder/store';
import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore';
import { useSocketStore } from '../../../../../../store/builder/store';
import { useSceneContext } from '../../../../../scene/sceneContext';
import { useProductContext } from '../../../../../simulation/products/productContext';
import { useVersionContext } from '../../../../version/versionContext';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../../../../functions/getUserData';
import { useLeftData, useTopData } from '../../../../../../store/visualization/useZone3DWidgetStore';
import { useSelectedAsset } from '../../../../../../store/simulation/useSimulationStore';
import { upsertProductOrEventApi } from '../../../../../../services/simulation/products/UpsertProductOrEventApi';

// import { deleteFloorItem } from '../../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';

export function useModelEventHandlers({
    boundingBox,
    groupRef,
    asset
}: {
    boundingBox: THREE.Box3 | null,
    groupRef: React.RefObject<THREE.Group>,
    asset: Asset
}) {
    const { controls, gl, camera } = useThree();
    const { activeTool } = useActiveTool();
    const { activeModule } = useModuleStore();
    const { toggleView } = useToggleView();
    const { subModule } = useSubModuleStore();
    const { socket } = useSocketStore();
    const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext();
    const { push3D } = undoRedo3DStore();
    const { getAssetById, removeAsset } = assetStore();
    const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
    const { removeEvent, getEventByModelUuid } = eventStore();
    const { getIsEventInProduct, addPoint, deleteEvent } = productStore();
    const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
    const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
    const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
    const { selectedProductStore } = useProductContext();
    const { selectedProduct } = selectedProductStore();
    const { selectedVersionStore } = useVersionContext();
    const { selectedVersion } = selectedVersionStore();
    const { projectId } = useParams();
    const { userId, organization } = getUserData();
    const leftDrag = useRef(false);
    const isLeftMouseDown = useRef(false);
    const rightDrag = useRef(false);
    const isRightMouseDown = useRef(false);
    const { setTop } = useTopData();
    const { setLeft } = useLeftData();

    const updateBackend = (
        productName: string,
        productUuid: string,
        projectId: string,
        eventData: EventsSchema
    ) => {
        upsertProductOrEventApi({
            productName: productName,
            productUuid: productUuid,
            projectId: projectId,
            eventDatas: eventData,
            versionId: selectedVersion?.versionId || '',
        });
    };

    useEffect(() => {
        if (!zoneAssetId) return
        if (zoneAssetId.id === asset.modelUuid) {
            handleDblClick(asset);
        }
    }, [zoneAssetId])

    useEffect(() => {
        if (!selectedFloorItem) {
            setZoneAssetId(null);
        }
    }, [selectedFloorItem])

    const handleDblClick = (asset: Asset) => {
        if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {

            const frontView = false;

            if (frontView) {
                const size = boundingBox.getSize(new THREE.Vector3());
                const center = boundingBox.getCenter(new THREE.Vector3());

                const front = new THREE.Vector3(0, 0, 1);
                groupRef.current.localToWorld(front);
                front.sub(groupRef.current.position).normalize();

                const distance = Math.max(size.x, size.y, size.z) * 2;
                const newPosition = center.clone().addScaledVector(front, distance);

                (controls as CameraControls).setPosition(newPosition.x, newPosition.y, newPosition.z, true);
                (controls as CameraControls).setTarget(center.x, center.y, center.z, true);
                (controls as CameraControls).fitToBox(groupRef.current, true, {
                    cover: true,
                    paddingTop: 5,
                    paddingLeft: 5,
                    paddingBottom: 5,
                    paddingRight: 5,
                });
            } else {

                const collisionPos = new THREE.Vector3();
                groupRef.current.getWorldPosition(collisionPos);

                const currentPos = new THREE.Vector3().copy(camera.position);

                const target = new THREE.Vector3();
                if (!controls) return;
                (controls as CameraControls).getTarget(target);
                const direction = new THREE.Vector3().subVectors(target, currentPos).normalize();

                const offsetDistance = 5;
                const newCameraPos = new THREE.Vector3().copy(collisionPos).sub(direction.multiplyScalar(offsetDistance));

                camera.position.copy(newCameraPos);
                (controls as CameraControls).setLookAt(newCameraPos.x, newCameraPos.y, newCameraPos.z, collisionPos.x, 0, collisionPos.z, true);
            }

            setSelectedFloorItem(groupRef.current);
        }
    };

    const handleClick = async (evt: ThreeEvent<MouseEvent>, asset: Asset) => {
        if (leftDrag.current || toggleView) return;
        if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {

            //REST

            // const response = await deleteFloorItem(organization, asset.modelUuid, asset.modelName);

            //SOCKET 

            const data = {
                organization,
                modelUuid: asset.modelUuid,
                modelName: asset.modelName,
                socketId: socket.id,
                userId,
                versionId: selectedVersion?.versionId || '',
                projectId
            }

            const response = socket.emit('v1:model-asset:delete', data)

            removeEvent(asset.modelUuid);
            const updatedEvents = deleteEvent(asset.modelUuid);

            updatedEvents.forEach((event) => {
                updateBackend(
                    selectedProduct.productName,
                    selectedProduct.productUuid,
                    projectId || '',
                    event
                );
            })

            if (response) {

                removeAsset(asset.modelUuid);

                push3D({
                    type: 'Scene',
                    actions: [
                        {
                            module: "builder",
                            actionType: "Asset-Delete",
                            asset: {
                                type: "Asset",
                                assetData: asset,
                                timeStap: new Date().toISOString()
                            }
                        }
                    ]
                });

                echo.success("Model Removed!");
            }

        } else if (activeModule === 'simulation' && subModule === "simulations" && activeTool === 'pen') {
            if (asset.eventData && asset.eventData.type === 'Conveyor') {
                const intersectedPoint = evt.point;
                const localPosition = groupRef.current?.worldToLocal(intersectedPoint.clone());
                if (localPosition) {
                    const conveyorPoint: ConveyorPointSchema = {
                        uuid: THREE.MathUtils.generateUUID(),
                        position: [localPosition?.x, localPosition?.y, localPosition?.z],
                        rotation: [0, 0, 0],
                        action: {
                            actionUuid: THREE.MathUtils.generateUUID(),
                            actionName: `Action 1`,
                            actionType: 'default',
                            material: 'Default Material',
                            delay: 0,
                            spawnInterval: 5,
                            spawnCount: 1,
                            triggers: []
                        }
                    }

                    const event = addPoint(selectedProduct.productUuid, asset.modelUuid, conveyorPoint);

                    if (event) {
                        updateBackend(
                            selectedProduct.productName,
                            selectedProduct.productUuid,
                            projectId || '',
                            event
                        );
                    }
                }
            }
        }
    };

    const handlePointerOver = useCallback((asset: Asset) => {
        if (activeTool === "delete" && activeModule === 'builder') {
            if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
                return;
            } else {
                setDeletableFloorItem(groupRef.current);
            }
        }
    }, [activeTool, activeModule, deletableFloorItem]);

    const handlePointerOut = useCallback((evt: ThreeEvent<MouseEvent>, asset: Asset) => {
        if (evt.intersections.length === 0 && activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
            setDeletableFloorItem(null);
        }
    }, [activeTool, deletableFloorItem]);

    const handleContextMenu = (asset: Asset, evt: ThreeEvent<MouseEvent>) => {
        if (rightDrag.current || toggleView) return;
        if (activeTool === "cursor" && subModule === 'simulations') {
            if (asset.modelUuid) {
                const canvasElement = gl.domElement;
                const isInProduct = getIsEventInProduct(selectedProduct.productUuid, asset.modelUuid);
                if (isInProduct) {
                    const event = getEventByModelUuid(asset.modelUuid);
                    if (event) {
                        setSelectedAsset(event);
                        const canvasRect = canvasElement.getBoundingClientRect();
                        const relativeX = evt.clientX - canvasRect.left;
                        const relativeY = evt.clientY - canvasRect.top;
                        setTop(relativeY);
                        setLeft(relativeX);
                    } else {
                        clearSelectedAsset();
                    }
                } else {
                    const event = getEventByModelUuid(asset.modelUuid);
                    if (event) {
                        setSelectedAsset(event)
                        const canvasRect = canvasElement.getBoundingClientRect();
                        const relativeX = evt.clientX - canvasRect.left;
                        const relativeY = evt.clientY - canvasRect.top;
                        setTop(relativeY);
                        setLeft(relativeX);
                    } else {
                        clearSelectedAsset()
                    }
                }
            } else {
                clearSelectedAsset()
            }
        } else {
            clearSelectedAsset()
        }
    }

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

        const onPointerDown = (evt: any) => {
            if (evt.button === 0) {
                isLeftMouseDown.current = true;
                leftDrag.current = false;
            }
            if (evt.button === 2) {
                isRightMouseDown.current = true;
                rightDrag.current = false;
            }
        };

        const onPointerMove = () => {
            if (isLeftMouseDown.current) {
                leftDrag.current = true;
            }
            if (isRightMouseDown.current) {
                rightDrag.current = true;
            }
        };

        const onPointerUp = (evt: any) => {
            if (evt.button === 0) {
                isLeftMouseDown.current = false;
            }
            if (evt.button === 2) {
                isRightMouseDown.current = false;
            }
        };

        canvasElement.addEventListener('pointerdown', onPointerDown);
        canvasElement.addEventListener('pointermove', onPointerMove);
        canvasElement.addEventListener('pointerup', onPointerUp);

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

    }, [gl])

    return {
        handleDblClick,
        handleClick,
        handlePointerOver,
        handlePointerOut,
        handleContextMenu
    };
}
