import * as THREE from 'three';
import { CameraControls } from '@react-three/drei';
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
import { useEffect, useState } from 'react';
import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import useModuleStore from '../../../../store/useModuleStore';
import { getUserData } from '../../../../functions/getUserData';
import { useVersionContext } from '../../version/versionContext';
import { useParams } from 'react-router-dom';
import { useSceneContext } from '../../../scene/sceneContext';

import { detectModifierKeys } from '../../../../utils/shortcutkeys/detectModifierKeys';
import handleDecalPositionSnap from '../functions/handleDecalPositionSnap';

import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';

export function useDecalEventHandlers({
    parent,
    decal,
    visible,
}: {
    parent: Wall | Floor;
    decal: Decal;
    visible: boolean;
}) {
    const { wallStore, floorStore } = useSceneContext();
    const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById, addDecal: addDecalToWall } = wallStore();
    const { removeDecal: removeDecalInFloor, updateDecalPosition: updateDecalPositionInFloor, getFloorById, addDecal: addDecalToFloor } = floorStore();
    const { setSelectedWall, setSelectedFloor, setSelectedDecal, setDeletableDecal, deletableDecal, selectedDecal, setDecalDragState, decalDragState } = useBuilderStore();
    const { toolMode } = useToolMode();
    const { toggleView } = useToggleView();
    const { activeModule } = useModuleStore();
    const { userId, organization } = getUserData();
    const { selectedVersionStore } = useVersionContext();
    const { selectedVersion } = selectedVersionStore();
    const { projectId } = useParams();
    const { socket } = useSocketStore();
    const [keyEvent, setKeyEvent] = useState<"Ctrl" | "">("");
    const { raycaster, pointer, camera, scene, gl, controls } = useThree();

    useFrame(() => {
        if (activeModule !== 'builder' || toggleView || !decalDragState.isDragging || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return;

        raycaster.setFromCamera(pointer, camera);
        const intersects = raycaster.intersectObjects(scene.children, true);

        const wallIntersect = intersects.find(i => i.object.userData?.wallUuid);
        const floorIntersect = intersects.find(i => i.object.userData?.floorUuid);

        let offset = decalDragState.dragOffset || new THREE.Vector3(0, 0, 0);

        if (wallIntersect) {
            const wallUuid = wallIntersect.object.userData.wallUuid;
            const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone());

            let finalPos;

            if (keyEvent === "Ctrl") {
                finalPos = handleDecalPositionSnap(point, offset, parent, decal, 0.05)
            } else {
                finalPos = point
            }

            if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === 'Wall') {
                updateDecalPositionInWall(decal.decalUuid, [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]]);
            } else if (decal.decalType.type === 'Wall' && wallUuid) {
                deleteDecal(decal.decalUuid, parent);

                const addedDecal = addDecalToWall(wallUuid, {
                    ...decal,
                    decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]],
                    decalType: { type: 'Wall', wallUuid: wallUuid }
                });

                if (addedDecal) {
                    setSelectedDecal({ decalMesh: null, decalData: addedDecal })
                }
            } else if (decal.decalType.type === 'Floor' && wallUuid) {
                deleteDecal(decal.decalUuid, parent);
                const wall = getWallById(wallUuid);
                if (!wall) return;

                const addedDecal = addDecalToWall(wallUuid, {
                    ...decal,
                    decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, wall.wallThickness / 2 + 0.001],
                    decalType: { type: 'Wall', wallUuid: wallUuid }
                });

                if (addedDecal) {
                    setSelectedDecal({ decalMesh: null, decalData: addedDecal })
                }
            }
        } else if (floorIntersect) {
            const floorUuid = floorIntersect.object.userData.floorUuid;
            const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone());

            let finalPos;

            if (keyEvent === "Ctrl") {
                finalPos = handleDecalPositionSnap(point, offset, parent, decal, 0.25)
            } else {
                finalPos = point
            }

            if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === 'Floor') {
                updateDecalPositionInFloor(decal.decalUuid, [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]]);
            } else if (decal.decalType.type === 'Floor' && floorUuid) {
                deleteDecal(decal.decalUuid, parent);

                const addedDecal = addDecalToFloor(floorUuid, {
                    ...decal,
                    decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]],
                    decalType: { type: 'Floor', floorUuid: floorUuid }
                });

                if (addedDecal) {
                    setSelectedDecal({ decalMesh: null, decalData: addedDecal })
                }
            } else if (decal.decalType.type === 'Wall' && floorUuid) {
                deleteDecal(decal.decalUuid, parent);
                const floor = getFloorById(floorUuid);
                if (!floor) return;

                const addedDecal = addDecalToFloor(floorUuid, {
                    ...decal,
                    decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, -0.001],
                    decalType: { type: 'Floor', floorUuid: floorUuid }
                });

                if (addedDecal) {
                    setSelectedDecal({ decalMesh: null, decalData: addedDecal })
                }
            }
        }
    });

    const handlePointerUp = () => {
        if (controls) {
            (controls as CameraControls).enabled = true;
        }
        if (decalDragState.isDragging) {
            setDecalDragState(false, null, null);

            if ('wallUuid' in parent) {
                setTimeout(() => {
                    const updatedWall = getWallById(parent.wallUuid);
                    if (updatedWall) {
                        if (projectId) {
                            if (!socket?.active) {
                                // API

                                upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
                            } else {

                                // SOCKET 

                                const data = {
                                    wallData: updatedWall,
                                    projectId: projectId,
                                    versionId: selectedVersion?.versionId || '',
                                    userId: userId,
                                    organization: organization
                                }

                                socket.emit('v1:model-Wall:add', data);
                            }
                        }
                    }
                }, 0)
            } else if ('floorUuid' in parent) {
                setTimeout(() => {
                    const updatedFloor = parent;

                    if (projectId && updatedFloor) {
                        if (!socket?.active) {
                            // API

                            upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
                        } else {

                            // SOCKET

                            const data = {
                                floorData: updatedFloor,
                                projectId: projectId,
                                versionId: selectedVersion?.versionId || '',
                                userId: userId,
                                organization: organization
                            }

                            socket.emit('v1:model-Floor:add', data);
                        }
                    }
                }, 0)
            }
        }
    };

    const deleteDecal = (decalUuid: string, parent: Wall | Floor) => {
        if ('wallUuid' in parent) {
            const updatedWall = removeDecalInWall(decalUuid);

            if (projectId && updatedWall) {
                if (!socket?.active) {
                    // API

                    upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
                } else {

                    // SOCKET 

                    const data = {
                        wallData: updatedWall,
                        projectId: projectId,
                        versionId: selectedVersion?.versionId || '',
                        userId: userId,
                        organization: organization
                    }

                    socket.emit('v1:model-Wall:add', data);
                }
            }
        } else if ('floorUuid' in parent) {
            const updatedFloor = removeDecalInFloor(decalUuid);

            if (projectId && updatedFloor) {
                if (!socket?.active) {
                    // API

                    upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
                } else {

                    // SOCKET

                    const data = {
                        floorData: updatedFloor,
                        projectId: projectId,
                        versionId: selectedVersion?.versionId || '',
                        userId: userId,
                        organization: organization
                    }

                    socket.emit('v1:model-Floor:add', data);
                }
            }
        }
    }

    const handlePointerDown = (e: ThreeEvent<MouseEvent>) => {
        if (visible && !toggleView && activeModule === 'builder') {
            if (e.object.userData.decalUuid && toolMode === 'cursor' && (selectedDecal && selectedDecal.decalData.decalUuid === decal.decalUuid)) {
                e.stopPropagation();
                setDecalDragState(true, decal.decalUuid, null);
                if (controls) {
                    (controls as CameraControls).enabled = false;
                }
                setSelectedWall(null);
                setSelectedFloor(null);

                const localIntersect = e.object.worldToLocal(e.point.clone());
                let dragOffset = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0);
                setDecalDragState(true, decal.decalUuid, dragOffset);
            }
        }
    };

    const handleDecalUp = (e: ThreeEvent<MouseEvent>) => {
        e.stopPropagation();
        if (visible && !toggleView && activeModule === 'builder') {
            if (!decalDragState.isDragging && !selectedDecal && e.object.userData.decalUuid && toolMode === 'cursor') {
                setSelectedDecal({ decalMesh: e.object, decalData: decal });
            }
        }
    }

    const handleClick = (e: ThreeEvent<MouseEvent>) => {
        if (visible && !toggleView && activeModule === 'builder') {
            if (e.object.userData.decalUuid) {
                e.stopPropagation();
                if (toolMode === 'cursor') {
                    setSelectedDecal({ decalMesh: e.object, decalData: decal });
                    setSelectedWall(null);
                    setSelectedFloor(null);
                } else if (toolMode === '3D-Delete') {
                    deleteDecal(e.object.userData.decalUuid, parent);
                }
            }
        }
    };

    const handlePointerEnter = (e: ThreeEvent<MouseEvent>) => {
        if (visible && !toggleView && activeModule === 'builder') {
            if (e.object.userData.decalUuid) {
                e.stopPropagation();
                if (toolMode === '3D-Delete') {
                    setDeletableDecal(e.object);
                }
            }
        }
    };

    const handlePointerLeave = (e: ThreeEvent<MouseEvent>) => {
        if (visible && !toggleView && activeModule === 'builder') {
            if (e.object.userData.decalUuid) {
                e.stopPropagation();
                if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) {
                    setDeletableDecal(null);
                }
            }
        }
    };

    const handlePointerMissed = () => {
        if (selectedDecal && selectedDecal.decalMesh && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) {
            setSelectedDecal(null);
            setKeyEvent("");
        }
    };

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

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

    const onKeyDown = (event: KeyboardEvent) => {
        const keyCombination = detectModifierKeys(event);
        if (keyCombination !== keyEvent) {
            if (keyCombination === "Ctrl") {
                setKeyEvent(keyCombination);
            } else {
                setKeyEvent("");
            }
        }
    }

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

        if (activeModule === 'builder' && !toggleView && selectedDecal && selectedDecal.decalData.decalUuid === decal.decalUuid) {
            canvasElement.addEventListener('pointerup', handlePointerUp);
            canvasElement?.addEventListener("keyup", onKeyUp);
            canvasElement.addEventListener("keydown", onKeyDown);
        }

        return () => {
            canvasElement.removeEventListener('pointerup', handlePointerUp);
            canvasElement?.removeEventListener("keyup", onKeyUp);
            canvasElement.removeEventListener("keydown", onKeyDown);
        };
    }, [gl, activeModule, toggleView, selectedDecal, camera, controls, visible, parent, decal, decalDragState]);

    return {
        handlePointerDown,
        handleDecalUp,
        handleClick,
        handlePointerEnter,
        handlePointerLeave,
        handlePointerMissed,
        deleteDecal
    };
}