import * as THREE from 'three';
import { useThree } from '@react-three/fiber';
import { useEffect, useMemo, useState } from "react";
import { DragControls, Tube } from '@react-three/drei';
import { useToolMode } from '../../../store/builder/store';
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import { useSceneContext } from '../../scene/sceneContext';
import * as Constants from '../../../types/world/worldConstants';

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

function Line({ points }: Readonly<LineProps>) {
    const [isHovered, setIsHovered] = useState(false);
    const { raycaster, camera, pointer, gl } = useThree();
    const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
    const [isDeletable, setIsDeletable] = useState(false);
    const { toolMode } = useToolMode();
    const { wallStore } = useSceneContext();
    const { removeWallByPoints, setPosition } = wallStore();
    const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
    const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();

    const path = useMemo(() => {
        const [start, end] = points.map(p => new THREE.Vector3(...p.position));
        return new THREE.LineCurve3(start, end);
    }, [points]);

    const colors = getColor(points[0]);

    function getColor(point: Point) {
        if (point.pointType === 'Aisle') {
            return {
                defaultLineColor: Constants.lineConfig.aisleColor,
                defaultDeleteColor: Constants.lineConfig.deleteColor,
            }
        } else if (point.pointType === 'Floor') {
            return {
                defaultLineColor: Constants.lineConfig.floorColor,
                defaultDeleteColor: Constants.lineConfig.deleteColor,
            }
        } else if (point.pointType === 'Wall') {
            return {
                defaultLineColor: Constants.lineConfig.wallColor,
                defaultDeleteColor: Constants.lineConfig.deleteColor,
            }
        } else if (point.pointType === 'Zone') {
            return {
                defaultLineColor: Constants.lineConfig.zoneColor,
                defaultDeleteColor: Constants.lineConfig.deleteColor,
            }
        } else {
            return {
                defaultLineColor: Constants.lineConfig.defaultColor,
                defaultDeleteColor: Constants.lineConfig.deleteColor,
            }
        }
    }

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

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

    const handlePointClick = (points: [Point, Point]) => {
        if (toolMode === '2D-Delete') {
            if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
                removeWallByPoints(points);
                setHoveredLine(null);
            }
            gl.domElement.style.cursor = 'default';
        }
    }

    const handleDrag = (points: [Point, Point]) => {
        if (toolMode === 'move' && isHovered && dragOffset) {
            raycaster.setFromCamera(pointer, camera);
            const intersectionPoint = new THREE.Vector3();
            const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);

            if (hit) {
                gl.domElement.style.cursor = 'move';
                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);

                setPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]);
                setPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]);
            }
        }
    };

    const handleDragStart = (points: [Point, Point]) => {
        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);
        }
    };

    const handleDragEnd = (points: [Point, Point]) => {
        gl.domElement.style.cursor = 'default';
        setDragOffset(null);
        if (toolMode !== 'move') return;
        if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') {
            // console.log('Wall after drag: ', points);
        }
    }

    return (
        <DragControls
            axisLock="y"
            autoTransform={false}
            onDragStart={() => handleDragStart(points)}
            onDrag={() => handleDrag(points)}
            onDragEnd={() => handleDragEnd(points)}
        >
            <Tube
                name={`${points[0].pointType}-Line`}
                key={`${points[0].pointUuid}-${points[1].pointUuid}`}
                uuid={`${points[0].pointUuid}-${points[1].pointUuid}`}
                userData={{ points, path }}
                args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
                onClick={() => {
                    handlePointClick(points);
                }}
                onPointerOver={() => {
                    if (!hoveredLine) {
                        setHoveredLine(points);
                        setIsHovered(true)
                        if (toolMode === 'move' && !hoveredPoint) {
                            gl.domElement.style.cursor = 'pointer';
                        }
                    }
                }}
                onPointerOut={() => {
                    if (hoveredLine) {
                        setHoveredLine(null);
                        gl.domElement.style.cursor = 'default';
                    }
                    setIsHovered(false)
                }}
            >
                <meshStandardMaterial color={isDeletable ? colors.defaultDeleteColor : colors.defaultLineColor} />
            </Tube>
        </DragControls >
    );
}

export default Line;