import * as THREE from 'three'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useSceneContext } from '../../../scene/sceneContext';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../version/versionContext';
import { getUserData } from '../../../../functions/getUserData';
import ReferencePoint from '../../point/reference/referencePoint';
import ReferenceZone from './referenceZone';

// import { upsertZoneApi } from '../../../../services/factoryBuilder/zone/upsertZoneApi';

function ZoneCreator() {
    const { scene, camera, raycaster, gl, pointer } = useThree();
    const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
    const { toggleView } = useToggleView();
    const { toolMode } = useToolMode();
    const { activeLayer } = useActiveLayer();
    const { socket } = useSocketStore();
    const { zoneStore } = useSceneContext();
    const { zones, addZone, getZonePointById, getZoneByPoints } = zoneStore();
    const drag = useRef(false);
    const isLeftMouseDown = useRef(false);
    const { selectedVersionStore } = useVersionContext();
    const { selectedVersion } = selectedVersionStore();
    const { userId, organization } = getUserData();
    const { projectId } = useParams();

    const [tempPoints, setTempPoints] = useState<Point[]>([]);
    const [isCreating, setIsCreating] = useState(false);
    const { zoneColor, zoneHeight, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore();


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

        const onMouseDown = (evt: any) => {
            if (evt.button === 0) {
                isLeftMouseDown.current = true;
                drag.current = false;
            }
        };

        const onMouseUp = (evt: any) => {
            if (evt.button === 0) {
                isLeftMouseDown.current = false;
            }
        };

        const onMouseMove = () => {
            if (isLeftMouseDown) {
                drag.current = true;
            }
        };

        const onMouseClick = () => {
            if (drag.current || !toggleView) return;

            raycaster.setFromCamera(pointer, camera);
            const intersectionPoint = new THREE.Vector3();
            let position = raycaster.ray.intersectPlane(plane, intersectionPoint);
            if (!position) return;

            const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Zone-Point');

            // const zoneIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Zone-Line');

            // if (zoneIntersect && !pointIntersects) {

            // }

            const newPoint: Point = {
                pointUuid: THREE.MathUtils.generateUUID(),
                pointType: 'Zone',
                position: [position.x, position.y, position.z],
                layer: activeLayer
            };

            if (snappedPosition && snappedPoint) {
                newPoint.pointUuid = snappedPoint.pointUuid;
                newPoint.position = snappedPosition;
                newPoint.layer = snappedPoint.layer;
            }

            if (snappedPoint && snappedPoint.pointUuid === tempPoints[tempPoints.length - 1]?.pointUuid) { return }

            if (snappedPosition && !snappedPoint) {
                newPoint.position = snappedPosition;
            }

            if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) {
                const zone: Zone = {
                    zoneUuid: THREE.MathUtils.generateUUID(),
                    zoneName: `Zone `,
                    points: tempPoints,
                    zoneColor,
                    zoneHeight,
                    viewPortPosition: [0, 0, 0],
                    viewPortTarget: [0, 0, 0]
                };

                addZone(zone);
                if (projectId) {

                    // API

                    // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone);

                    // SOCKET

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

                    socket.emit('v1:zone:add', data);

                }
                setTempPoints([]);
                setIsCreating(false);
            } else if (isCreating && snappedPoint && !tempPoints.some(p => p.pointUuid === snappedPoint.pointUuid)) {
                setTempPoints(prev => [...prev, newPoint]);
                setIsCreating(true);
            } else if (pointIntersects) {
                if (tempPoints.length > 2 && isCreating && pointIntersects.object.uuid === tempPoints[0].pointUuid) {
                    const zone: Zone = {
                        zoneUuid: THREE.MathUtils.generateUUID(),
                        zoneName: "Zone",
                        points: tempPoints,
                        zoneColor,
                        zoneHeight,
                        viewPortPosition: [0, 0, 0],
                        viewPortTarget: [0, 0, 0]
                    };

                    addZone(zone);
                    if (projectId) {

                        // API

                        // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone);

                        // SOCKET

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

                        socket.emit('v1:zone:add', data);

                    }
                    setTempPoints([]);
                    setIsCreating(false);
                } else if (tempPoints.length === 0 || (tempPoints.length > 1 && !tempPoints.slice(1).some(p => p.pointUuid === pointIntersects.object.uuid))) {
                    tempPoints.push(pointIntersects.object.userData as Point);
                    setIsCreating(true);
                }
            } else {
                setTempPoints(prev => [...prev, newPoint]);
                setIsCreating(true);
            }

        };

        const onContext = (event: any) => {
            event.preventDefault();
            if (isCreating) {
                if (tempPoints.length >= 3) {
                    const zone: Zone = {
                        zoneUuid: THREE.MathUtils.generateUUID(),
                        zoneName: "Zone",
                        points: tempPoints,
                        zoneColor,
                        zoneHeight,
                        viewPortPosition: [0, 0, 0],
                        viewPortTarget: [0, 0, 0]
                    };

                    addZone(zone);
                    if (projectId) {

                        // API

                        // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone);

                        // SOCKET

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

                        socket.emit('v1:zone:add', data);

                    }
                }
                setTempPoints([]);
                setIsCreating(false);
            }
        };

        if (toolMode === "Zone" && toggleView) {
            if (tempPoints.length === 0) {
                setSnappedPosition(null);
                setSnappedPoint(null);
            }
            canvasElement.addEventListener("mousedown", onMouseDown);
            canvasElement.addEventListener("mouseup", onMouseUp);
            canvasElement.addEventListener("mousemove", onMouseMove);
            canvasElement.addEventListener("click", onMouseClick);
            canvasElement.addEventListener("contextmenu", onContext);
        } else {
            setTempPoints([]);
            setIsCreating(false);
            canvasElement.removeEventListener("mousedown", onMouseDown);
            canvasElement.removeEventListener("mouseup", onMouseUp);
            canvasElement.removeEventListener("mousemove", onMouseMove);
            canvasElement.removeEventListener("click", onMouseClick);
            canvasElement.removeEventListener("contextmenu", onContext);
        }

        return () => {
            canvasElement.removeEventListener("mousedown", onMouseDown);
            canvasElement.removeEventListener("mouseup", onMouseUp);
            canvasElement.removeEventListener("mousemove", onMouseMove);
            canvasElement.removeEventListener("click", onMouseClick);
            canvasElement.removeEventListener("contextmenu", onContext);
        };
    }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, getZoneByPoints, zoneColor, zoneHeight, snappedPosition, snappedPoint]);

    return (
        <>
            {toggleView &&
                <>
                    <group name='Zone-Reference-Points-Group'>
                        {tempPoints.map((point) => (
                            <ReferencePoint key={point.pointUuid} point={point} />
                        ))}
                    </group>

                    {tempPoints.length > 0 &&
                        <ReferenceZone tempPoints={tempPoints} />
                    }
                </>
            }
        </>
    )
}

export default ZoneCreator