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 * as Constants from '../../../../types/world/worldConstants';
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 getClosestIntersection from '../../line/helpers/getClosestIntersection';
import ReferencePoint from '../../point/reference/referencePoint';
import ReferenceWall from './referenceWall';

// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
// import { deleteWallApi } from '../../../../services/factoryBuilder/wall/deleteWallApi';

function WallCreator() {
    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 { wallStore, undoRedo2DStore } = useSceneContext();
    const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore();
    const { push2D } = undoRedo2DStore();
    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 { wallThickness, wallHeight, insideMaterial, outsideMaterial, 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 === 'Wall-Point');

            const wallIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Wall-Line');

            if (wallIntersect && !pointIntersects) {
                const wall = getWallByPoints(wallIntersect.object.userData.points);
                if (wall) {
                    const ThroughPoint = wallIntersect.object.userData.path.getPoints(Constants.lineConfig.lineIntersectionPoints);
                    let intersectionPoint = getClosestIntersection(ThroughPoint, wallIntersect.point);
                    const point1Vec = new THREE.Vector3(...wall.points[0].position);
                    const point2Vec = new THREE.Vector3(...wall.points[1].position);

                    const lineDir = new THREE.Vector3().subVectors(point2Vec, point1Vec).normalize();

                    const point1ToIntersect = new THREE.Vector3().subVectors(intersectionPoint, point1Vec);

                    const dotProduct = point1ToIntersect.dot(lineDir);
                    const projection = new THREE.Vector3().copy(lineDir).multiplyScalar(dotProduct).add(point1Vec);

                    const lineLength = point1Vec.distanceTo(point2Vec);
                    let t = point1Vec.distanceTo(projection) / lineLength;
                    t = Math.max(0, Math.min(1, t));

                    const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t);

                    removeWall(wall.wallUuid);

                    if (projectId) {

                        // API

                        // deleteWallApi(projectId, selectedVersion?.versionId || '', wall.wallUuid);

                        // SOCKET

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

                        socket.emit('v1:model-Wall:delete', data);

                    }

                    const point1: Point = {
                        pointUuid: wall.points[0].pointUuid,
                        pointType: 'Wall',
                        position: wall.points[0].position,
                        layer: wall.points[0].layer
                    };

                    const point2: Point = {
                        pointUuid: wall.points[1].pointUuid,
                        pointType: 'Wall',
                        position: wall.points[1].position,
                        layer: wall.points[1].layer
                    };

                    const newPoint: Point = {
                        pointUuid: THREE.MathUtils.generateUUID(),
                        pointType: 'Wall',
                        position: closestPoint.toArray(),
                        layer: activeLayer
                    };

                    if (tempPoints.length === 0) {
                        const wall2: Wall = {
                            wallUuid: THREE.MathUtils.generateUUID(),
                            points: [point1, newPoint],
                            outsideMaterial: insideMaterial,
                            insideMaterial: outsideMaterial,
                            wallThickness: wallThickness,
                            wallHeight: wallHeight,
                            decals: []
                        }

                        addWall(wall2);

                        // API

                        // if (projectId) {
                        //     upsertWallApi(projectId, selectedVersion?.versionId || '', wall2);
                        // }

                        // SOCKET

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

                        socket.emit('v1:model-Wall:add', data);

                        const wall3: Wall = {
                            wallUuid: THREE.MathUtils.generateUUID(),
                            points: [point2, newPoint],
                            outsideMaterial: insideMaterial,
                            insideMaterial: outsideMaterial,
                            wallThickness: wallThickness,
                            wallHeight: wallHeight,
                            decals: []
                        }

                        addWall(wall3);

                        push2D({
                            type: 'Draw',
                            actions: [
                                {
                                    actionType: 'Lines-Create',
                                    points: [
                                        {
                                            type: 'Wall',
                                            lineData: wall3,
                                            timeStamp: new Date().toISOString(),
                                        }, {
                                            type: 'Wall',
                                            lineData: wall2,
                                            timeStamp: new Date().toISOString(),
                                        }
                                    ]
                                }, {
                                    actionType: 'Line-Delete',
                                    point: {
                                        type: 'Wall',
                                        lineData: wall,
                                        timeStamp: new Date().toISOString(),
                                    }
                                }
                            ],
                        })

                        // API

                        // if (projectId) {
                        //     upsertWallApi(projectId, selectedVersion?.versionId || '', wall3);
                        // }

                        // SOCKET

                        const data2 = {
                            wallData: wall3,
                            projectId: projectId,
                            versionId: selectedVersion?.versionId || '',
                            userId: userId,
                            organization: organization
                        }

                        socket.emit('v1:model-Wall:add', data2);

                        setTempPoints([newPoint]);
                        setIsCreating(true);
                    } else {
                        const wall1: Wall = {
                            wallUuid: THREE.MathUtils.generateUUID(),
                            points: [tempPoints[0], newPoint],
                            outsideMaterial: insideMaterial,
                            insideMaterial: outsideMaterial,
                            wallThickness: wallThickness,
                            wallHeight: wallHeight,
                            decals: []
                        }

                        addWall(wall1);

                        // API

                        // if (projectId) {
                        //     upsertWallApi(projectId, selectedVersion?.versionId || '', wall1);
                        // }

                        // SOCKET

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

                        socket.emit('v1:model-Wall:add', data);

                        const wall2: Wall = {
                            wallUuid: THREE.MathUtils.generateUUID(),
                            points: [point1, newPoint],
                            outsideMaterial: insideMaterial,
                            insideMaterial: outsideMaterial,
                            wallThickness: wallThickness,
                            wallHeight: wallHeight,
                            decals: []
                        }

                        addWall(wall2);

                        // API

                        // if (projectId) {
                        //     upsertWallApi(projectId, selectedVersion?.versionId || '', wall2);
                        // }

                        // SOCKET

                        const data1 = {
                            wallData: wall2,
                            projectId: projectId,
                            versionId: selectedVersion?.versionId || '',
                            userId: userId,
                            organization: organization
                        }

                        socket.emit('v1:model-Wall:add', data1);

                        const wall3: Wall = {
                            wallUuid: THREE.MathUtils.generateUUID(),
                            points: [point2, newPoint],
                            outsideMaterial: insideMaterial,
                            insideMaterial: outsideMaterial,
                            wallThickness: wallThickness,
                            wallHeight: wallHeight,
                            decals: []
                        }

                        addWall(wall3);

                        push2D({
                            type: 'Draw',
                            actions: [
                                {
                                    actionType: 'Lines-Create',
                                    points: [
                                        {
                                            type: 'Wall',
                                            lineData: wall3,
                                            timeStamp: new Date().toISOString(),
                                        }, {
                                            type: 'Wall',
                                            lineData: wall1,
                                            timeStamp: new Date().toISOString(),
                                        }, {
                                            type: 'Wall',
                                            lineData: wall2,
                                            timeStamp: new Date().toISOString(),
                                        }
                                    ]
                                }, {
                                    actionType: 'Line-Delete',
                                    point: {
                                        type: 'Wall',
                                        lineData: wall,
                                        timeStamp: new Date().toISOString(),
                                    }
                                }
                            ],
                        })

                        // API

                        // if (projectId) {
                        //     upsertWallApi(projectId, selectedVersion?.versionId || '', wall3);
                        // }

                        // SOCKET

                        const data3 = {
                            wallData: wall3,
                            projectId: projectId,
                            versionId: selectedVersion?.versionId || '',
                            userId: userId,
                            organization: organization
                        }

                        socket.emit('v1:model-Wall:add', data3);

                        setTempPoints([newPoint]);
                    }

                    return;
                }
            }

            const newPoint: Point = {
                pointUuid: THREE.MathUtils.generateUUID(),
                pointType: 'Wall',
                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[0]?.pointUuid) { return }

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

            if (pointIntersects && !snappedPoint) {
                const point = getWallPointById(pointIntersects.object.uuid);
                if (point) {
                    newPoint.pointUuid = point.pointUuid;
                    newPoint.position = point.position;
                    newPoint.layer = point.layer;
                }
            }

            if (tempPoints.length === 0) {
                setTempPoints([newPoint]);
                setIsCreating(true);
            } else {
                const wall: Wall = {
                    wallUuid: THREE.MathUtils.generateUUID(),
                    points: [tempPoints[0], newPoint],
                    outsideMaterial: insideMaterial,
                    insideMaterial: outsideMaterial,
                    wallThickness: wallThickness,
                    wallHeight: wallHeight,
                    decals: []
                }

                addWall(wall);

                push2D({
                    type: 'Draw',
                    actions: [
                        {
                            actionType: 'Line-Create',
                            point: {
                                type: 'Wall',
                                lineData: wall,
                                timeStamp: new Date().toISOString(),
                            }
                        }
                    ],
                })

                // API

                // if (projectId) {
                //     upsertWallApi(projectId, selectedVersion?.versionId || '', wall);
                // }

                // SOCKET

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

                socket.emit('v1:model-Wall:add', data);

                setTempPoints([newPoint]);
            }

        };

        const onContext = (event: any) => {
            event.preventDefault();
            if (isCreating) {
                setTempPoints([]);
                setIsCreating(false);
            }
        };

        if (toolMode === "Wall" && 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, addWall, getWallPointById, wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint, selectedVersion?.versionId]);

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

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

export default WallCreator