import * as THREE from "three";
import { useParams } from "react-router-dom";
import { ThreeEvent, useThree } from "@react-three/fiber";
import { useCallback, useEffect, useMemo, useState } 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 useWallAssetResponseHandler from "../../../collaboration/responseHandler/useWallAssetResponseHandler";
import useWallResponseHandler from "../../../collaboration/responseHandler/useWallResponseHandler";

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 { deleteZoneApi } from "../../../../services/factoryBuilder/zone/deleteZoneApi";
import { upsertZoneApi } from "../../../../services/factoryBuilder/zone/upsertZoneApi";
import { upsertWallAssetApi } from "../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi";
import { deleteWallAssetApi } from "../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi";

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

interface LineProps {
    points: [Point, Point];
}

export function useLineEventHandler({ points }: Readonly<LineProps>) {
    const [isHovered, setIsHovered] = useState(false);
    const { raycaster, camera, pointer } = useThree();
    const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
    const [isDeletable, setIsDeletable] = useState(false);
    const { builderSocket } = useSocketStore();
    const { toolMode } = useToolMode();
    const { wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore, versionStore } = useSceneContext();
    const { push2D } = undoRedo2DStore();
    const { getWallAssetsByWall } = wallAssetStore();
    const { peekRemoveWallByPoints, setPosition: setWallPosition, getWallByPoints, getConnectedWallsByWallId } = wallStore();
    const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId, getFloorsByPoints } = floorStore();
    const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId, getZonesByPoints } = zoneStore();
    const { updateWallAssetInScene, removeWallAssetFromScene } = useWallAssetResponseHandler();
    const { updateWallInScene, removeWallFromScene } = useWallResponseHandler();
    const { selectedVersion } = versionStore();
    const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();
    const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
    const { selectedPoints } = useSelectedPoints();
    const { projectId } = useParams();
    const { userId, organization } = getUserData();

    const [initialPositions, setInitialPositions] = 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 start = new THREE.Vector3(...points[0].position);
                const end = new THREE.Vector3(...points[1].position);
                const midPoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);

                const delta = new THREE.Vector3().subVectors(positionWithOffset, midPoint);

                const newStart = new THREE.Vector3().addVectors(start, delta);
                const newEnd = new THREE.Vector3().addVectors(end, delta);

                if (points[0].pointType === "Wall" && points[1].pointType === "Wall") {
                    setWallPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
                    setWallPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
                }
                if (points[0].pointType === "Floor" && points[1].pointType === "Floor") {
                    setFloorPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
                    setFloorPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
                }
                if (points[0].pointType === "Zone" && points[1].pointType === "Zone") {
                    setZonePosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
                    setZonePosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
                }
            }
        }
    }, [camera, toolMode, isHovered, dragOffset, points]);

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

        if (hit && !hoveredPoint) {
            const start = new THREE.Vector3(...points[0].position);
            const end = new THREE.Vector3(...points[1].position);
            const midPoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);

            const offset = new THREE.Vector3().subVectors(midPoint, hit);
            setDragOffset(offset);

            if (points[0].pointType === "Wall" && points[1].pointType === "Wall") {
                const wall = getWallByPoints(points);
                if (wall) {
                    const walls = getConnectedWallsByWallId(wall.wallUuid, false);
                    setInitialPositions({ walls });
                }
            } else if (points[0].pointType === "Floor") {
                const floors = getFloorsByPointId(points[0].pointUuid);
                setInitialPositions({ floors });
            } else if (points[0].pointType === "Zone") {
                const zones = getZonesByPointId(points[0].pointUuid);
                setInitialPositions({ zones });
            }
        }
    }, [camera, points, hoveredPoint]);

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

        if (points[0].pointType === "Wall" && points[1].pointType === "Wall") {
            const wall = getWallByPoints(points);
            if (!wall) return;
            const updatedWalls = getConnectedWallsByWallId(wall.wallUuid, false);

            if (updatedWalls.length > 0 && projectId) {
                updatedWalls.forEach((updatedWall) => {
                    const initialWall = initialPositions.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 (initialPositions.walls && initialPositions.walls.length > 0) {
                    const updatedPoints = initialPositions.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 (points[0].pointType === "Floor" && points[1].pointType === "Floor") {
            const updatedFloors1 = getFloorsByPointId(points[0].pointUuid);
            const updatedFloors2 = getFloorsByPointId(points[1].pointUuid);
            const updatedFloors = [...updatedFloors1, ...updatedFloors2].filter((floor, index, self) => index === self.findIndex((f) => f.floorUuid === floor.floorUuid));

            if (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 (initialPositions.floors && initialPositions.floors.length > 0) {
                    const updatedPoints = initialPositions.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 (points[0].pointType === "Zone" && points[1].pointType === "Zone") {
            const updatedZones1 = getZonesByPointId(points[0].pointUuid);
            const updatedZones2 = getZonesByPointId(points[1].pointUuid);
            const updatedZones = [...updatedZones1, ...updatedZones2].filter((zone, index, self) => index === self.findIndex((z) => z.zoneUuid === zone.zoneUuid));

            if (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 (initialPositions.zones && initialPositions.zones.length > 0) {
                    const updatedPoints = initialPositions.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,
                            },
                        ],
                    });
                }
            }
        }
    }, [builderSocket, points, toolMode, dragOffset, projectId, selectedVersion, userId, organization]);

    const handleLineClick = useCallback(() => {
        if (toolMode === "2D-Delete") {
            if (points[0].pointType === "Wall" && points[1].pointType === "Wall") {
                const removedWall = peekRemoveWallByPoints(points);
                if (removedWall && projectId) {
                    const assetsOnWall = getWallAssetsByWall(removedWall.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 (!builderSocket?.connected) {
                        // API

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

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

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

                    push2D({
                        type: "Draw",
                        actions: [
                            {
                                actionType: "Line-Delete",
                                point: {
                                    type: "Wall",
                                    lineData: removedWall,
                                    timeStamp: new Date().toISOString(),
                                },
                            },
                        ],
                    });
                }

                setHoveredLine(null);
            }
            if (points[0].pointType === "Floor" && points[1].pointType === "Floor") {
                const Floors = getFloorsByPoints(points);
                const { removedFloors, updatedFloors } = removeFloorByPoints(points);
                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,
                            },
                        ],
                    });
                }

                setHoveredLine(null);
            }
            if (points[0].pointType === "Zone" && points[1].pointType === "Zone") {
                const Zones = getZonesByPoints(points);
                const { removedZones, updatedZones } = removeZoneByPoints(points);
                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,
                            },
                        ],
                    });
                }

                setHoveredLine(null);
            }
            handleCanvasCursors("default");
        }
    }, [builderSocket, points, toolMode, projectId, selectedVersion, userId, organization]);

    const handlePointerOver = useCallback(
        (e: ThreeEvent<MouseEvent>) => {
            if (selectedPoints.length === 0 && e.buttons === 0 && !e.ctrlKey) {
                setHoveredLine(points);
                setIsHovered(true);
                if (toolMode === "move" && !hoveredPoint) {
                    handleCanvasCursors("grab");
                }
            }
        },
        [selectedPoints, toolMode, hoveredPoint]
    );

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

    useEffect(() => {
        if (toolMode === "2D-Delete") {
            if (isHovered && !hoveredPoint) {
                setIsDeletable(true);
            } else {
                setIsDeletable(false);
            }
        } else {
            setIsDeletable(false);
        }
    }, [isHovered, toolMode, hoveredPoint]);

    useEffect(() => {
        if (hoveredLine && (hoveredLine[0].pointUuid !== points[0].pointUuid || hoveredLine[1].pointUuid !== points[1].pointUuid)) {
            setIsHovered(false);
        }
    }, [hoveredLine]);

    return {
        isHovered,
        isDeletable,
        handleDrag,
        handleDragStart,
        handleDragEnd,
        handleLineClick,
        handlePointerOver,
        handlePointerOut,
    };
}
