import * as THREE from "three";
import { useParams } from "react-router-dom";
import { ThreeEvent, useThree } from "@react-three/fiber";
import { useState, useEffect, useMemo, useCallback } from "react";
import { useToolMode } from "../../../../store/builder/store";
import { useSocketStore } from "../../../../store/socket/useSocketStore";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { useSceneContext } from "../../../scene/sceneContext";
import { useSelectedPoints } from "../../../../store/simulation/useSimulationStore";
import { usePointSnapping } from "../helpers/usePointSnapping";
import useWallAssetResponseHandler from "../../../collaboration/responseHandler/useWallAssetResponseHandler";
import useWallResponseHandler from "../../../collaboration/responseHandler/useWallResponseHandler";

import { upsertAisleApi } from "../../../../services/factoryBuilder/aisle/upsertAisleApi";
import { deleteAisleApi } from "../../../../services/factoryBuilder/aisle/deleteAisleApi";
import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
import { deleteWallApi } from "../../../../services/factoryBuilder/wall/deleteWallApi";
import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
import { deleteFloorApi } from "../../../../services/factoryBuilder/floor/deleteFloorApi";
import { upsertZoneApi } from "../../../../services/factoryBuilder/zone/upsertZoneApi";
import { deleteZoneApi } from "../../../../services/factoryBuilder/zone/deleteZoneApi";
import { upsertWallAssetApi } from "../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi";
import { deleteWallAssetApi } from "../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi";

import { getUserData } from "../../../../functions/getUserData";
import { handleCanvasCursors } from "../../../../utils/mouseUtils/handleCanvasCursors";
import { calculateAssetTransformationOnWall } from "../../wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall";

export function usePointEventHandler({ point }: { point: Point }) {
    const { raycaster, camera, pointer } = useThree();
    const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
    const [isHovered, setIsHovered] = useState(false);
    const [isDeleteHovered, setIsDeleteHovered] = useState(false);
    const [isSelected, setIsSelected] = useState(false);
    const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
    const { builderSocket } = useSocketStore();
    const { toolMode } = useToolMode();
    const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore, versionStore } = useSceneContext();
    const { push2D } = undoRedo2DStore();
    const { getWallAssetsByWall } = wallAssetStore();
    const { updateWallAssetInScene, removeWallAssetFromScene } = useWallAssetResponseHandler();
    const { updateWallInScene, removeWallFromScene } = useWallResponseHandler();
    const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
    const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
    const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
    const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore();
    const { selectedVersion } = versionStore();
    const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({
        uuid: point.pointUuid,
        pointType: point.pointType,
        position: point.position,
    });
    const { hoveredPoint, hoveredLine, setHoveredPoint } = useBuilderStore();
    const { selectedPoints } = useSelectedPoints();
    const { userId, organization } = getUserData();
    const { projectId } = useParams();

    const [initialStates, setInitialStates] = useState<{
        aisles?: Aisle[];
        walls?: Wall[];
        floors?: Floor[];
        zones?: Zone[];
    }>({});

    const handleDrag = useCallback(() => {
        if (toolMode === "move" && isHovered && dragOffset) {
            raycaster.setFromCamera(pointer, camera);
            const intersectionPoint = new THREE.Vector3();
            const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);

            if (hit) {
                handleCanvasCursors("grabbing");
                const positionWithOffset = new THREE.Vector3().addVectors(hit, dragOffset);
                const newPosition: [number, number, number] = [positionWithOffset.x, positionWithOffset.y, positionWithOffset.z];

                if (point.pointType === "Aisle") {
                    const aisleSnapped = snapAisleAngle(newPosition);
                    const finalSnapped = snapAislePoint(aisleSnapped.position);
                    setAislePosition(point.pointUuid, finalSnapped.position);
                } else if (point.pointType === "Wall") {
                    const wallSnapped = snapWallAngle(newPosition);
                    const finalSnapped = snapWallPoint(wallSnapped.position);
                    setWallPosition(point.pointUuid, finalSnapped.position);
                } else if (point.pointType === "Floor") {
                    const floorSnapped = snapFloorAngle(newPosition);
                    const finalSnapped = snapFloorPoint(floorSnapped.position);
                    setFloorPosition(point.pointUuid, finalSnapped.position);
                } else if (point.pointType === "Zone") {
                    const zoneSnapped = snapZoneAngle(newPosition);
                    const finalSnapped = snapZonePoint(zoneSnapped.position);
                    setZonePosition(point.pointUuid, finalSnapped.position);
                }
            }
        }
    }, [toolMode, isHovered, dragOffset, camera, raycaster, plane, point]);

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

        if (hit) {
            const currentPosition = new THREE.Vector3(...point.position);
            const offset = new THREE.Vector3().subVectors(currentPosition, hit);
            setDragOffset(offset);

            if (point.pointType === "Aisle") {
                const aisles = getAislesByPointId(point.pointUuid);
                setInitialStates({ aisles });
            } else if (point.pointType === "Wall") {
                const walls = getWallsByPointId(point.pointUuid);
                setInitialStates({ walls });
            } else if (point.pointType === "Floor") {
                const floors = getFloorsByPointId(point.pointUuid);
                setInitialStates({ floors });
            } else if (point.pointType === "Zone") {
                const zones = getZonesByPointId(point.pointUuid);
                setInitialStates({ zones });
            }
        }
    }, [raycaster, camera, plane, point]);

    const handleDragEnd = useCallback(() => {
        handleCanvasCursors("default");
        setDragOffset(null);
        if (toolMode !== "move") return;

        if (point.pointType === "Aisle") {
            const updatedAisles = getAislesByPointId(point.pointUuid);
            if (updatedAisles.length > 0 && projectId) {
                updatedAisles.forEach((updatedAisle) => {
                    if (!builderSocket?.connected) {
                        // API

                        upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || "");
                    } else {
                        // SOCKET

                        builderSocket.emit("v1:model-aisle:add", {
                            projectId: projectId,
                            versionId: selectedVersion?.versionId || "",
                            userId: userId,
                            organization: organization,
                            aisleUuid: updatedAisle.aisleUuid,
                            points: updatedAisle.points,
                            type: updatedAisle.type,
                        });
                    }
                });

                if (initialStates.aisles && initialStates.aisles.length > 0) {
                    const updatedPoints = initialStates.aisles.map((aisle) => ({
                        type: "Aisle" as const,
                        lineData: aisle,
                        newData: updatedAisles.find((a) => a.aisleUuid === aisle.aisleUuid),
                        timeStamp: new Date().toISOString(),
                    }));

                    push2D({
                        type: "Draw",
                        actions: [
                            {
                                actionType: "Lines-Update",
                                points: updatedPoints,
                            },
                        ],
                    });
                }
            }
        } else if (point.pointType === "Wall") {
            const updatedWalls = getWallsByPointId(point.pointUuid);
            if (updatedWalls && updatedWalls.length > 0 && projectId) {
                updatedWalls.forEach((updatedWall) => {
                    const initialWall = initialStates.walls?.find((w) => w.wallUuid === updatedWall.wallUuid);

                    if (initialWall) {
                        const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid);

                        assetsOnWall.forEach((asset) => {
                            const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall);

                            const updatedWallAsset: WallAsset = {
                                ...asset,
                                position: [position[0], asset.position[1], position[2]],
                                rotation: rotation,
                            };

                            if (projectId && updatedWallAsset) {
                                if (!builderSocket?.connected) {
                                    // API

                                    upsertWallAssetApi(projectId, selectedVersion?.versionId || "", updatedWallAsset)
                                        .then((data) => {
                                            if (!data.message) {
                                                echo.error(`Error updating wallAsset ${updatedWallAsset.modelName} on the wall`);
                                                return;
                                            }
                                            if (data.message === "WallAsset Updated Successfully" && data.data) {
                                                updateWallAssetInScene(updatedWallAsset, () => {
                                                    echo.log(`Updated wallAsset: ${updatedWallAsset.modelUuid} on the wall`);
                                                });
                                            } else {
                                                echo.error(`Error updating wallAsset ${updatedWallAsset.modelName} on the wall`);
                                            }
                                        })
                                        .catch(() => {
                                            echo.error(`Error updating wallAsset ${updatedWallAsset.modelName} on the wall`);
                                        });
                                } else {
                                    // SOCKET

                                    const data = {
                                        wallAssetData: updatedWallAsset,
                                        projectId: projectId,
                                        versionId: selectedVersion?.versionId || "",
                                        userId: userId,
                                        organization: organization,
                                    };

                                    builderSocket.emit("v1:wall-asset:add", data);
                                }
                            }
                        });
                    }

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

                        upsertWallApi(projectId, selectedVersion?.versionId || "", updatedWall)
                            .then((data) => {
                                if (!data.message || !data.data) {
                                    echo.error(`Error updating wall`);
                                    return;
                                }
                                if (data.message === "Wall Updated Successfully") {
                                    updateWallInScene(updatedWall, () => {
                                        echo.log(`Updated wall: ${updatedWall.wallUuid}`);
                                    });
                                } else {
                                    echo.error(`Error updating wall: ${updatedWall.wallUuid}`);
                                }
                            })
                            .catch(() => {
                                echo.error(`Error updating wall: ${updatedWall.wallUuid}`);
                            });
                    } else {
                        // SOCKET

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

                        builderSocket.emit("v1:model-Wall:add", data);
                    }
                });
            }

            if (initialStates.walls && initialStates.walls.length > 0) {
                const updatedPoints = initialStates.walls.map((wall) => ({
                    type: "Wall" as const,
                    lineData: wall,
                    newData: updatedWalls.find((w) => w.wallUuid === wall.wallUuid),
                    timeStamp: new Date().toISOString(),
                }));

                push2D({
                    type: "Draw",
                    actions: [
                        {
                            actionType: "Lines-Update",
                            points: updatedPoints,
                        },
                    ],
                });
            }
        } else if (point.pointType === "Floor") {
            const updatedFloors = getFloorsByPointId(point.pointUuid);
            if (updatedFloors && updatedFloors.length > 0 && projectId) {
                updatedFloors.forEach((updatedFloor) => {
                    if (!builderSocket?.connected) {
                        // API

                        upsertFloorApi(projectId, selectedVersion?.versionId || "", updatedFloor);
                    } else {
                        // SOCKET

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

                        builderSocket.emit("v1:model-Floor:add", data);
                    }
                });
            }

            if (initialStates.floors && initialStates.floors.length > 0) {
                const updatedPoints = initialStates.floors.map((floor) => ({
                    type: "Floor" as const,
                    lineData: floor,
                    newData: updatedFloors.find((f) => f.floorUuid === floor.floorUuid),
                    timeStamp: new Date().toISOString(),
                }));

                push2D({
                    type: "Draw",
                    actions: [
                        {
                            actionType: "Lines-Update",
                            points: updatedPoints,
                        },
                    ],
                });
            }
        } else if (point.pointType === "Zone") {
            const updatedZones = getZonesByPointId(point.pointUuid);
            if (updatedZones && updatedZones.length > 0 && projectId) {
                updatedZones.forEach((updatedZone) => {
                    if (!builderSocket?.connected) {
                        // API

                        upsertZoneApi(projectId, selectedVersion?.versionId || "", updatedZone);
                    } else {
                        // SOCKET

                        const data = {
                            zoneData: updatedZone,
                            projectId: projectId,
                            versionId: selectedVersion?.versionId || "",
                            userId: userId,
                            organization: organization,
                        };

                        builderSocket.emit("v1:zone:add", data);
                    }
                });
            }

            if (initialStates.zones && initialStates.zones.length > 0) {
                const updatedPoints = initialStates.zones.map((zone) => ({
                    type: "Zone" as const,
                    lineData: zone,
                    newData: updatedZones.find((z) => z.zoneUuid === zone.zoneUuid),
                    timeStamp: new Date().toISOString(),
                }));

                push2D({
                    type: "Draw",
                    actions: [
                        {
                            actionType: "Lines-Update",
                            points: updatedPoints,
                        },
                    ],
                });
            }
        }

        setInitialStates({});
    }, [toolMode, projectId, builderSocket, selectedVersion, userId, organization, initialStates, point]);

    const handlePointClick = useCallback(
        (e: ThreeEvent<MouseEvent>) => {
            e.stopPropagation();
            if (toolMode === "2D-Delete") {
                if (point.pointType === "Aisle") {
                    const removedAisles = removeAislePoint(point.pointUuid);
                    setHoveredPoint(null);
                    if (removedAisles.length > 0) {
                        removedAisles.forEach((aisle) => {
                            if (projectId) {
                                if (!builderSocket?.connected) {
                                    // API

                                    deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || "");
                                } else {
                                    // SOCKET

                                    const data = {
                                        projectId: projectId,
                                        versionId: selectedVersion?.versionId || "",
                                        userId: userId,
                                        organization: organization,
                                        aisleUuid: aisle.aisleUuid,
                                    };

                                    builderSocket.emit("v1:model-aisle:delete", data);
                                }
                            }
                        });

                        const removedAislesData = removedAisles.map((aisle) => ({
                            type: "Aisle" as const,
                            lineData: aisle,
                            timeStamp: new Date().toISOString(),
                        }));

                        push2D({
                            type: "Draw",
                            actions: [
                                {
                                    actionType: "Lines-Delete",
                                    points: removedAislesData,
                                },
                            ],
                        });
                    }
                }
                if (point.pointType === "Wall") {
                    const removedWalls = removeWallPoint(point.pointUuid);
                    if (removedWalls.length > 0) {
                        setHoveredPoint(null);
                        removedWalls.forEach((wall) => {
                            const assetsOnWall = getWallAssetsByWall(wall.wallUuid);

                            assetsOnWall.forEach((asset) => {
                                if (projectId && asset) {
                                    if (!builderSocket?.connected) {
                                        // API

                                        deleteWallAssetApi(projectId, selectedVersion?.versionId || "", asset.modelUuid, asset.wallUuid)
                                            .then((data) => {
                                                if (!data.message || !data.data) {
                                                    echo.error(`Error removing wallAsset from wall`);
                                                    return;
                                                }
                                                if (data.message === "WallAsset Deleted Successfully") {
                                                    removeWallAssetFromScene(asset.modelUuid, () => {
                                                        echo.log(`Removed wallAsset: ${asset.modelName} along with wall`);
                                                    });
                                                } else {
                                                    echo.error(`Error removing wallAsset: ${asset.modelName} from wall`);
                                                }
                                            })
                                            .catch(() => {
                                                echo.error(`Error removing wallAsset: ${asset.modelName}`);
                                            });
                                    } else {
                                        // SOCKET

                                        const data = {
                                            projectId: projectId,
                                            versionId: selectedVersion?.versionId || "",
                                            userId: userId,
                                            organization: organization,
                                            modelUuid: asset.modelUuid,
                                            wallUuid: asset.wallUuid,
                                        };

                                        builderSocket.emit("v1:wall-asset:delete", data);
                                    }
                                }
                            });

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

                                    deleteWallApi(projectId, selectedVersion?.versionId || "", wall.wallUuid)
                                        .then((data) => {
                                            if (!data.message || !data.data) {
                                                echo.error(`Error removing wall`);
                                                return;
                                            }
                                            if (data.message === "Wall Deleted Successfully") {
                                                removeWallFromScene(wall.wallUuid, () => {
                                                    echo.log(`Removed wall: ${wall.wallUuid}`);
                                                });
                                            } else {
                                                echo.error(`Error removing wall: ${wall.wallUuid}`);
                                            }
                                        })
                                        .catch(() => {
                                            echo.error(`Error removing wall: ${wall.wallUuid}`);
                                        });
                                } else {
                                    // SOCKET

                                    const data = {
                                        wallUuid: wall.wallUuid,
                                        projectId: projectId,
                                        versionId: selectedVersion?.versionId || "",
                                        userId: userId,
                                        organization: organization,
                                    };

                                    builderSocket.emit("v1:model-Wall:delete", data);
                                }
                            }
                        });

                        const removedWallsData = removedWalls.map((wall) => ({
                            type: "Wall" as const,
                            lineData: wall,
                            timeStamp: new Date().toISOString(),
                        }));

                        push2D({
                            type: "Draw",
                            actions: [
                                {
                                    actionType: "Lines-Delete",
                                    points: removedWallsData,
                                },
                            ],
                        });
                    }
                }
                if (point.pointType === "Floor") {
                    const Floors = getFloorsByPointId(point.pointUuid);
                    const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid);
                    setHoveredPoint(null);
                    if (removedFloors.length > 0) {
                        removedFloors.forEach((floor) => {
                            if (projectId) {
                                if (!builderSocket?.connected) {
                                    // API

                                    deleteFloorApi(projectId, selectedVersion?.versionId || "", floor.floorUuid);
                                } else {
                                    // SOCKET

                                    const data = {
                                        floorUuid: floor.floorUuid,
                                        projectId: projectId,
                                        versionId: selectedVersion?.versionId || "",
                                        userId: userId,
                                        organization: organization,
                                    };

                                    builderSocket.emit("v1:model-Floor:delete", data);
                                }
                            }
                        });

                        const removedFloorsData = removedFloors.map((floor) => ({
                            type: "Floor" as const,
                            lineData: floor,
                            timeStamp: new Date().toISOString(),
                        }));

                        push2D({
                            type: "Draw",
                            actions: [
                                {
                                    actionType: "Lines-Delete",
                                    points: removedFloorsData,
                                },
                            ],
                        });
                    }
                    if (updatedFloors.length > 0) {
                        updatedFloors.forEach((floor) => {
                            if (projectId) {
                                if (!builderSocket?.connected) {
                                    // API

                                    upsertFloorApi(projectId, selectedVersion?.versionId || "", floor);
                                } else {
                                    // SOCKET

                                    const data = {
                                        floorData: floor,
                                        projectId: projectId,
                                        versionId: selectedVersion?.versionId || "",
                                        userId: userId,
                                        organization: organization,
                                    };

                                    builderSocket.emit("v1:model-Floor:add", data);
                                }
                            }
                        });

                        const updatedFloorsData = updatedFloors.map((floor) => ({
                            type: "Floor" as const,
                            lineData: Floors.find((f) => f.floorUuid === floor.floorUuid) || floor,
                            newData: floor,
                            timeStamp: new Date().toISOString(),
                        }));

                        push2D({
                            type: "Draw",
                            actions: [
                                {
                                    actionType: "Lines-Update",
                                    points: updatedFloorsData,
                                },
                            ],
                        });
                    }
                }
                if (point.pointType === "Zone") {
                    const Zones = getZonesByPointId(point.pointUuid);
                    const { removedZones, updatedZones } = removeZonePoint(point.pointUuid);
                    setHoveredPoint(null);
                    if (removedZones.length > 0) {
                        removedZones.forEach((zone) => {
                            if (projectId) {
                                if (!builderSocket?.connected) {
                                    // API

                                    deleteZoneApi(projectId, selectedVersion?.versionId || "", zone.zoneUuid);
                                } else {
                                    // SOCKET

                                    const data = {
                                        zoneUuid: zone.zoneUuid,
                                        projectId: projectId,
                                        versionId: selectedVersion?.versionId || "",
                                        userId: userId,
                                        organization: organization,
                                    };

                                    builderSocket.emit("v1:zone:delete", data);
                                }
                            }
                        });

                        const removedZonesData = removedZones.map((zone) => ({
                            type: "Zone" as const,
                            lineData: zone,
                            timeStamp: new Date().toISOString(),
                        }));

                        push2D({
                            type: "Draw",
                            actions: [
                                {
                                    actionType: "Lines-Delete",
                                    points: removedZonesData,
                                },
                            ],
                        });
                    }
                    if (updatedZones.length > 0) {
                        updatedZones.forEach((zone) => {
                            if (projectId) {
                                if (!builderSocket?.connected) {
                                    // API

                                    upsertZoneApi(projectId, selectedVersion?.versionId || "", zone);
                                } else {
                                    // SOCKET

                                    const data = {
                                        zoneData: zone,
                                        projectId: projectId,
                                        versionId: selectedVersion?.versionId || "",
                                        userId: userId,
                                        organization: organization,
                                    };

                                    builderSocket.emit("v1:zone:add", data);
                                }
                            }
                        });

                        const updatedZonesData = updatedZones.map((zone) => ({
                            type: "Zone" as const,
                            lineData: Zones.find((z) => z.zoneUuid === zone.zoneUuid) || zone,
                            newData: zone,
                            timeStamp: new Date().toISOString(),
                        }));

                        push2D({
                            type: "Draw",
                            actions: [
                                {
                                    actionType: "Lines-Update",
                                    points: updatedZonesData,
                                },
                            ],
                        });
                    }
                }
                handleCanvasCursors("default");
            }
        },
        [toolMode, projectId, builderSocket, selectedVersion, userId, organization, point]
    );

    const handlePointerOver = useCallback(
        (e: ThreeEvent<MouseEvent>) => {
            if (!hoveredPoint && selectedPoints.length === 0 && e.buttons === 0 && !e.ctrlKey) {
                setHoveredPoint(point);
                if (toolMode === "move") {
                    setIsHovered(true);
                    handleCanvasCursors("grab");
                } else if (toolMode === "2D-Delete") {
                    setIsDeleteHovered(true);
                }
            }
        },
        [hoveredPoint, selectedPoints, toolMode, point, setHoveredPoint]
    );

    const handlePointerOut = useCallback(() => {
        if (hoveredPoint) {
            setHoveredPoint(null);
            if (!hoveredLine && toolMode === "move") {
                handleCanvasCursors("default");
            }
        }
        setIsHovered(false);
        setIsDeleteHovered(false);
    }, [hoveredPoint, hoveredLine, setHoveredPoint]);

    useEffect(() => {
        if (hoveredPoint && hoveredPoint.pointUuid !== point.pointUuid) {
            setIsHovered(false);
        }
    }, [hoveredPoint]);

    useEffect(() => {
        if (selectedPoints.length > 0 && selectedPoints.some((selectedPoint) => selectedPoint.userData.pointUuid && selectedPoint.userData.pointUuid === point.pointUuid)) {
            setIsSelected(true);
        } else {
            setIsSelected(false);
        }
    }, [selectedPoints]);

    return {
        isHovered,
        isDeleteHovered,
        isSelected,
        handleDrag,
        handleDragStart,
        handleDragEnd,
        handlePointClick,
        handlePointerOver,
        handlePointerOut,
    };
}
