import { useEffect, useRef, useState } from 'react'
import { useGLTF } from '@react-three/drei';
import { useFrame, useThree } from '@react-three/fiber';
import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
import { useProductContext } from '../../../products/productContext';
import { useSceneContext } from '../../../../scene/sceneContext';
import { Group, Plane, Vector2, Vector3 } from 'three';
import { useVersionContext } from '../../../../builder/version/versionContext';
import { useParams } from 'react-router-dom';
import startPoint from "../../../../../assets/gltf-glb/ui/human-ui-green.glb";
import startEnd from "../../../../../assets/gltf-glb/ui/human-ui-orange.glb";
import assembly from "../../../../../assets/gltf-glb/ui/human-ui-assembly.glb";
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';

function HumanUi() {
    const { scene: startScene } = useGLTF(startPoint) as any;
    const { scene: endScene } = useGLTF(startEnd) as any;
    const { scene: assemblyScene } = useGLTF(assembly) as any;
    const startMarker = useRef<Group>(null);
    const endMarker = useRef<Group>(null);
    const assemblyMarker = useRef<Group>(null);
    const outerGroup = useRef<Group>(null);
    const prevMousePos = useRef({ x: 0, y: 0 });
    const { controls, raycaster, camera } = useThree();
    const { selectedEventSphere } = useSelectedEventSphere();
    const { selectedProductStore } = useProductContext();
    const { humanStore, productStore } = useSceneContext();
    const { selectedProduct } = selectedProductStore();
    const { humans, getHumanById } = humanStore();
    const { updateEvent } = productStore();
    const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]);
    const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]);
    const [assemblyPosition, setAssemblyPosition] = useState<[number, number, number]>([0, 1, 0]);
    const [startRotation, setStartRotation] = useState<[number, number, number]>([0, Math.PI, 0]);
    const [assemblyRotation, setAssemblyRotation] = useState<[number, number, number]>([0, 0, 0]);
    const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]);
    const { isDragging, setIsDragging } = useIsDragging();
    const { isRotating, setIsRotating } = useIsRotating();
    const plane = useRef(new Plane(new Vector3(0, 1, 0), 0));
    const dragOffset = useRef(new Vector3());

    const [selectedHumanData, setSelectedHumanData] = useState<{
        position: [number, number, number];
        rotation: [number, number, number];
    }>({ position: [0, 0, 0], rotation: [0, 0, 0] });

    const { selectedAction } = useSelectedAction();
    const { selectedVersionStore } = useVersionContext();
    const { selectedVersion } = selectedVersionStore();
    const { projectId } = useParams();

    const selectedHuman = selectedEventSphere ? getHumanById(selectedEventSphere.userData.modelUuid) : null;
    const actionType = selectedHuman?.point?.action?.actionType || null;
    const isAssembly = actionType === 'assembly';

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

    useEffect(() => {
        if (!selectedEventSphere) return;

        const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
        if (!selectedHuman || !selectedHuman.point?.action) return;

        setSelectedHumanData({
            position: selectedHuman.position,
            rotation: selectedHuman.rotation,
        });

        if (outerGroup.current) {
            outerGroup.current.position.set(
                selectedHuman.position[0],
                selectedHuman.position[1],
                selectedHuman.position[2]
            );
        }

        const action = selectedHuman.point.action;

        if (isAssembly) {
            if (action.assemblyPoint?.position && outerGroup.current) {
                const worldPos = new Vector3(...action.assemblyPoint.position);
                const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
                setAssemblyPosition([localPosition.x, 1, localPosition.z]);
                setAssemblyRotation(action.assemblyPoint.rotation || [0, 0, 0]);
            } else {
                setAssemblyPosition([0, 1, 0]);
                setAssemblyRotation([0, 0, 0]);
            }
        } else {
            if (action.pickUpPoint?.position && outerGroup.current) {
                const worldPos = new Vector3(...action.pickUpPoint.position);
                const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
                setStartPosition([localPosition.x, 1, localPosition.z]);
                setStartRotation(action.pickUpPoint.rotation || [0, 0, 0]);
            } else {
                setStartPosition([0, 1, 0]);
                setStartRotation([0, Math.PI, 0]);
            }

            if (action.dropPoint?.position && outerGroup.current) {
                const worldPos = new Vector3(...action.dropPoint.position);
                const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
                setEndPosition([localPosition.x, 1, localPosition.z]);
                setEndRotation(action.dropPoint.rotation || [0, Math.PI, 0]);
            } else {
                setEndPosition([0, 1, 0]);
                setEndRotation([0, 0, 0]);
            }
        }
    }, [selectedEventSphere, outerGroup.current, selectedAction, humans]);

    const handlePointerDown = (
        e: any,
        state: "start" | "end" | "assembly",
        rotation: "start" | "end" | "assembly"
    ) => {
        e.stopPropagation();
        const intersection = new Vector3();
        const pointer = new Vector2((e.clientX / window.innerWidth) * 2 - 1, -(e.clientY / window.innerHeight) * 2 + 1);
        raycaster.setFromCamera(pointer, camera);
        const intersects = raycaster.ray.intersectPlane(plane.current, intersection);

        if (e.object.parent.name === "handle") {
            const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
            const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
            prevMousePos.current = { x: normalizedX, y: normalizedY };
            setIsRotating(rotation);
            setIsDragging(null);
        } else {
            setIsDragging(state);
            setIsRotating(null);
        }

        if (intersects) {
            let localPoint: Vector3 | null = null;
            if (outerGroup.current) {
                localPoint = outerGroup.current.worldToLocal(intersection.clone());
            }
            const marker =
                state === "start" ? startMarker.current :
                    state === "end" ? endMarker.current :
                        assemblyMarker.current;
            if (marker && localPoint) {
                const markerPos = new Vector3().copy(marker.position);
                dragOffset.current.copy(markerPos.sub(localPoint));
            }
        }

        if (controls) (controls as any).enabled = false;
    };

    const handlePointerUp = () => {
        (controls as any).enabled = true;
        setIsDragging(null);
        setIsRotating(null);

        if (!selectedEventSphere?.userData.modelUuid || !selectedAction?.actionId) return;

        const selectedHuman = getHumanById(selectedEventSphere.userData.modelUuid);
        if (!selectedHuman || !outerGroup.current) return;

        let updatedAction;

        if (isAssembly) {
            if (!assemblyMarker.current) return;

            const worldPosAssembly = new Vector3(...assemblyPosition);
            const globalAssemblyPosition = outerGroup.current.localToWorld(worldPosAssembly.clone());

            updatedAction = {
                ...selectedHuman.point.action,
                assemblyPoint: {
                    position: [globalAssemblyPosition.x, globalAssemblyPosition.y, globalAssemblyPosition.z] as [number, number, number],
                    rotation: assemblyRotation
                },
            };
        } else {
            if (!startMarker.current || !endMarker.current) return;

            const worldPosStart = new Vector3(...startPosition);
            const globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone());

            const worldPosEnd = new Vector3(...endPosition);
            const globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone());

            updatedAction = {
                ...selectedHuman.point.action,
                pickUpPoint: {
                    position: [globalStartPosition.x, globalStartPosition.y, globalStartPosition.z] as [number, number, number],
                    rotation: startRotation,
                },
                dropPoint: {
                    position: [globalEndPosition.x, globalEndPosition.y, globalEndPosition.z] as [number, number, number],
                    rotation: endRotation,
                },
            };
        }

        const event = updateEvent(
            selectedProduct.productUuid,
            selectedEventSphere.userData.modelUuid,
            {
                ...selectedHuman,
                point: {
                    ...selectedHuman.point,
                    action: updatedAction,
                },
            }
        );

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

    useFrame(() => {
        if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
        const intersectPoint = new Vector3();
        const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint);
        if (!intersects) return;

        const localPoint = outerGroup.current.worldToLocal(intersectPoint.clone()).add(dragOffset.current);

        if (isDragging === "start") {
            setStartPosition([localPoint.x, 1, localPoint.z]);
        } else if (isDragging === "end") {
            setEndPosition([localPoint.x, 1, localPoint.z]);
        } else if (isDragging === "assembly") {
            setAssemblyPosition([localPoint.x, 1, localPoint.z]);
        }
    });

    useFrame((state) => {
        if (!isRotating) return;
        const currentPointerX = state.pointer.x;
        const deltaX = currentPointerX - prevMousePos.current.x;
        prevMousePos.current.x = currentPointerX;

        const marker =
            isRotating === "start" ? startMarker.current :
                isRotating === "end" ? endMarker.current :
                    assemblyMarker.current;

        if (marker) {
            const rotationSpeed = 10;
            marker.rotation.y += deltaX * rotationSpeed;

            if (isRotating === "start") {
                setStartRotation([
                    marker.rotation.x,
                    marker.rotation.y,
                    marker.rotation.z,
                ]);
            } else if (isRotating === "end") {
                setEndRotation([
                    marker.rotation.x,
                    marker.rotation.y,
                    marker.rotation.z,
                ]);
            } else {
                setAssemblyRotation([
                    marker.rotation.x,
                    marker.rotation.y,
                    marker.rotation.z,
                ]);
            }
        }
    });

    useEffect(() => {
        const handleGlobalPointerUp = () => {
            setIsDragging(null);
            setIsRotating(null);
            if (controls) (controls as any).enabled = true;
            handlePointerUp();
        };

        if (isDragging || isRotating) {
            window.addEventListener("pointerup", handleGlobalPointerUp);
        }

        return () => {
            window.removeEventListener("pointerup", handleGlobalPointerUp);
        };
    }, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, assemblyPosition, assemblyRotation]);

    return (
        <>
            {selectedHumanData && (
                <group
                    position={selectedHumanData.position}
                    ref={outerGroup}
                    rotation={[0, Math.PI, 0]}
                >
                    {isAssembly ? (
                        <primitive
                            ref={assemblyMarker}
                            object={assemblyScene}
                            position={assemblyPosition}
                            rotation={assemblyRotation}
                            onPointerDown={(e: any) => {
                                if (e.object.parent.name === "handle") {
                                    handlePointerDown(e, "assembly", "assembly");
                                } else {
                                    handlePointerDown(e, "assembly", "assembly");
                                }
                            }}
                            onPointerMissed={() => {
                                setIsDragging(null);
                                setIsRotating(null);
                                if (controls) (controls as any).enabled = true;
                            }}
                        />
                    ) : (
                        <>
                            <primitive
                                name="startMarker"
                                object={startScene}
                                ref={startMarker}
                                position={startPosition}
                                rotation={startRotation}
                                onPointerDown={(e: any) => {
                                    if (e.object.parent.name === "handle") {
                                        handlePointerDown(e, "start", "start");
                                    } else {
                                        handlePointerDown(e, "start", "start");
                                    }
                                }}
                                onPointerMissed={() => {
                                    setIsDragging(null);
                                    setIsRotating(null);
                                    if (controls) (controls as any).enabled = true;
                                }}
                            />

                            <primitive
                                name="endMarker"
                                object={endScene}
                                ref={endMarker}
                                position={endPosition}
                                rotation={endRotation}
                                onPointerDown={(e: any) => {
                                    if (e.object.parent.name === "handle") {
                                        handlePointerDown(e, "end", "end");
                                    } else {
                                        handlePointerDown(e, "end", "end");
                                    }
                                }}
                                onPointerMissed={() => {
                                    setIsDragging(null);
                                    setIsRotating(null);
                                    if (controls) (controls as any).enabled = true;
                                }}
                            />
                        </>
                    )}
                </group>
            )}
        </>
    );
}

export default HumanUi;