import { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
import { useProductStore } from "../../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore";
import { TransformControls } from "@react-three/drei";
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore";
import { useThree } from "@react-three/fiber";
import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore";
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";

function PointsCreator() {
    const { gl, raycaster, scene, pointer, camera } = useThree();
    const { subModule } = useSubModuleStore();
    const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
    const { getEventByModelUuid: getEventByModelUuidFromProduct, updatePoint: updatePointFromProduct, getEventByModelUuid: getEventByModelUuidFromProduct2, getPointByUuid: getPointByUuidFromProduct } = useProductStore();
    const { selectedProduct } = useSelectedProduct();
    const { activeModule } = useModuleStore();
    const transformRef = useRef<any>(null);
    const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
    const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
    const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere();
    const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
    const { isPlaying } = usePlayButtonStore();

    const email = localStorage.getItem('email')
    const organization = (email!.split("@")[1]).split(".")[0];

    const updateBackend = (
        productName: string,
        productId: string,
        organization: string,
        eventData: EventsSchema
    ) => {
        upsertProductOrEventApi({
            productName: productName,
            productId: productId,
            organization: organization,
            eventDatas: eventData
        })
    }

    useEffect(() => {
        if (selectedEventSphere) {
            const eventData = getEventByModelUuid(
                selectedEventSphere.userData.modelUuid
            );

            if (eventData) {
                setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid);
            } else {
                clearSelectedEventData();
            }
        } else {
            clearSelectedEventData();
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [selectedEventSphere]);

    useEffect(() => {
        const handleKeyDown = (e: KeyboardEvent) => {
            const keyCombination = detectModifierKeys(e);
            if (!selectedEventSphere) return;
            if (keyCombination === "G") {
                setTransformMode((prev) => (prev === "translate" ? null : "translate"));
            }
            if (keyCombination === "R") {
                setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
            }
        };

        window.addEventListener("keydown", handleKeyDown);
        return () => window.removeEventListener("keydown", handleKeyDown);
    }, [selectedEventSphere]);

    const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
        let point: PointsScheme = JSON.parse(
            JSON.stringify(
                getPointByUuid(
                    selectedEventSphere.userData.modelUuid,
                    selectedEventSphere.userData.pointUuid
                )
            )
        );
        if (point) {
            point.position = [
                selectedEventSphere.position.x,
                selectedEventSphere.position.y,
                selectedEventSphere.position.z,
            ];
            point.rotation = [
                selectedEventSphere.rotation.x,
                selectedEventSphere.rotation.y,
                selectedEventSphere.rotation.z,
            ];

            const event = getEventByModelUuidFromProduct(selectedProduct.productId, selectedEventSphere.userData.modelUuid);

            if (event && selectedProduct.productId !== '') {

                const updatedPoint = JSON.parse(
                    JSON.stringify(
                        getPointByUuidFromProduct(selectedProduct.productId, selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)
                    )
                );
                if (updatedPoint) {
                    updatedPoint.position = point.position;
                    updatedPoint.rotation = point.rotation;

                    const updatedEvent = updatePointFromProduct(
                        selectedProduct.productId,
                        selectedEventSphere.userData.modelUuid,
                        selectedEventSphere.userData.pointUuid,
                        updatedPoint
                    )
                    if (updatedEvent) {
                        updatePoint(
                            selectedEventSphere.userData.modelUuid,
                            selectedEventSphere.userData.pointUuid,
                            point
                        )
                        updateBackend(
                            selectedProduct.productName,
                            selectedProduct.productId,
                            organization,
                            updatedEvent
                        );
                    }
                }
            }
        }
    };

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

        let drag = false;
        let isMouseDown = false;

        const onMouseDown = () => {
            isMouseDown = true;
            drag = false;
        };

        const onMouseUp = () => {
            if (selectedEventSphere && !drag) {
                raycaster.setFromCamera(pointer, camera);
                const intersects = raycaster
                    .intersectObjects(scene.children, true)
                    .filter((intersect) => intersect.object.name === "Event-Sphere");
                if (intersects.length === 0) {
                    clearSelectedEventSphere();
                    setTransformMode(null);
                }
            }
        };

        const onMouseMove = () => {
            if (isMouseDown) {
                drag = true;
            }
        };

        if (subModule === "mechanics") {
            canvasElement.addEventListener("mousedown", onMouseDown);
            canvasElement.addEventListener("mouseup", onMouseUp);
            canvasElement.addEventListener("mousemove", onMouseMove);
        }

        return () => {
            canvasElement.removeEventListener("mousedown", onMouseDown);
            canvasElement.removeEventListener("mouseup", onMouseUp);
            canvasElement.removeEventListener("mousemove", onMouseMove);
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [gl, subModule, selectedEventSphere]);

    return (
        <>
            {activeModule === "simulation" && (
                <>
                    <group name="EventPointsGroup" visible={!isPlaying}>
                        {events.map((event, index) => {
                            const updatedEvent = selectedProduct.productId !== ''
                                ? getEventByModelUuidFromProduct2(selectedProduct.productId, event.modelUuid)
                                : null;

                            const usedEvent = updatedEvent || event;

                            if (usedEvent.type === "transfer") {
                                return (
                                    <group
                                        key={`${index}-${usedEvent.modelUuid}`}
                                        position={usedEvent.position}
                                        rotation={usedEvent.rotation}
                                    >
                                        {usedEvent.points.map((point, j) => (
                                            <mesh
                                                name="Event-Sphere"
                                                uuid={point.uuid}
                                                ref={(el) => (sphereRefs.current[point.uuid] = el!)}
                                                onClick={(e) => {
                                                    e.stopPropagation();
                                                    setSelectedEventSphere(
                                                        sphereRefs.current[point.uuid]
                                                    );
                                                }}
                                                key={`${index}-${point.uuid}`}
                                                position={new THREE.Vector3(...point.position)}
                                                userData={{
                                                    modelUuid: usedEvent.modelUuid,
                                                    pointUuid: point.uuid,
                                                }}
                                            >
                                                <sphereGeometry args={[0.1, 16, 16]} />
                                                <meshStandardMaterial color="orange" />
                                            </mesh>
                                        ))}
                                    </group>
                                );
                            } else if (usedEvent.type === "vehicle") {
                                const point = usedEvent.point;
                                return (
                                    <group
                                        key={`${index}-${usedEvent.modelUuid}`}
                                        position={usedEvent.position}
                                        rotation={usedEvent.rotation}
                                    >
                                        <mesh
                                            name="Event-Sphere"
                                            uuid={point.uuid}
                                            ref={(el) => (sphereRefs.current[point.uuid] = el!)}
                                            onClick={(e) => {
                                                e.stopPropagation();
                                                setSelectedEventSphere(
                                                    sphereRefs.current[point.uuid]
                                                );
                                            }}
                                            position={new THREE.Vector3(...point.position)}
                                            userData={{
                                                modelUuid: usedEvent.modelUuid,
                                                pointUuid: point.uuid,
                                            }}
                                        >
                                            <sphereGeometry args={[0.1, 16, 16]} />
                                            <meshStandardMaterial color="blue" />
                                        </mesh>
                                    </group>
                                );
                            } else if (usedEvent.type === "roboticArm") {
                                const point = usedEvent.point;
                                return (
                                    <group
                                        key={`${index}-${usedEvent.modelUuid}`}
                                        position={usedEvent.position}
                                        rotation={usedEvent.rotation}
                                    >
                                        <mesh
                                            name="Event-Sphere"
                                            uuid={point.uuid}
                                            ref={(el) => (sphereRefs.current[point.uuid] = el!)}
                                            onClick={(e) => {
                                                e.stopPropagation();
                                                setSelectedEventSphere(
                                                    sphereRefs.current[point.uuid]
                                                );
                                            }}
                                            position={new THREE.Vector3(...point.position)}
                                            userData={{
                                                modelUuid: usedEvent.modelUuid,
                                                pointUuid: point.uuid,
                                            }}
                                        >
                                            <sphereGeometry args={[0.1, 16, 16]} />
                                            <meshStandardMaterial color="green" />
                                        </mesh>
                                    </group>
                                );
                            } else if (usedEvent.type === "machine") {
                                const point = usedEvent.point;
                                return (
                                    <group
                                        key={`${index}-${usedEvent.modelUuid}`}
                                        position={usedEvent.position}
                                        rotation={usedEvent.rotation}
                                    >
                                        <mesh
                                            name="Event-Sphere"
                                            uuid={point.uuid}
                                            ref={(el) => (sphereRefs.current[point.uuid] = el!)}
                                            onClick={(e) => {
                                                e.stopPropagation();
                                                setSelectedEventSphere(
                                                    sphereRefs.current[point.uuid]
                                                );
                                            }}
                                            position={new THREE.Vector3(...point.position)}
                                            userData={{
                                                modelUuid: usedEvent.modelUuid,
                                                pointUuid: point.uuid,
                                            }}
                                        >
                                            <sphereGeometry args={[0.1, 16, 16]} />
                                            <meshStandardMaterial color="purple" />
                                        </mesh>
                                    </group>
                                );
                            } else if (usedEvent.type === "storageUnit") {
                                const point = usedEvent.point;
                                return (
                                    <group
                                        key={`${index}-${usedEvent.modelUuid}`}
                                        position={usedEvent.position}
                                        rotation={usedEvent.rotation}
                                    >
                                        <mesh
                                            name="Event-Sphere"
                                            uuid={point.uuid}
                                            ref={(el) => (sphereRefs.current[point.uuid] = el!)}
                                            onClick={(e) => {
                                                e.stopPropagation();
                                                setSelectedEventSphere(
                                                    sphereRefs.current[point.uuid]
                                                );
                                            }}
                                            position={new THREE.Vector3(...point.position)}
                                            userData={{
                                                modelUuid: usedEvent.modelUuid,
                                                pointUuid: point.uuid,
                                            }}
                                        >
                                            <sphereGeometry args={[0.1, 16, 16]} />
                                            <meshStandardMaterial color="red" />
                                        </mesh>
                                    </group>
                                );
                            } else {
                                return null;
                            }
                        })}
                    </group>

                    {selectedEventSphere && transformMode && (
                        <TransformControls
                            ref={transformRef}
                            object={selectedEventSphere}
                            mode={transformMode}
                            onMouseUp={(e) => {
                                updatePointToState(selectedEventSphere);
                            }}
                        />
                    )}
                </>
            )}
        </>
    );
}

export default PointsCreator;
