import React, { useEffect, useRef } from "react";
import { Vector3, Raycaster, BufferGeometry, LineBasicMaterial, Line, Object3D, Mesh } from "three";
import { useThree, useFrame } from "@react-three/fiber";
import { Group } from "three";
import { Html } from "@react-three/drei";
import * as THREE from "three";

interface DistanceFindingControlsProps {
    boundingBoxRef: React.RefObject<Mesh>;
}

const DistanceFindingControls = ({ boundingBoxRef }: DistanceFindingControlsProps) => {
    const { camera, scene } = useThree();


    // Refs for measurement lines
    const line1 = useRef<Line>(null);
    const line2 = useRef<Line>(null);
    const line3 = useRef<Line>(null);
    const line4 = useRef<Line>(null);
    const line5 = useRef<Line>(null);

    // Refs for measurement text labels
    const textPosX = useRef<Group>(null);
    const textNegX = useRef<Group>(null);
    const textPosZ = useRef<Group>(null);
    const textNegZ = useRef<Group>(null);
    const textPosY = useRef<Group>(null);

    // Store line geometries to avoid recreation
    const lineGeometries = useRef({
        posX: new BufferGeometry(),
        negX: new BufferGeometry(),
        posZ: new BufferGeometry(),
        negZ: new BufferGeometry(),
        posY: new BufferGeometry()
    });


    useFrame(() => {
        if (!boundingBoxRef?.current) return;

        boundingBoxRef.current.geometry.computeBoundingBox();
        const bbox = boundingBoxRef.current.geometry.boundingBox;

        if (!bbox) return;

        const size = {
            x: bbox.max.x - bbox.min.x,
            y: bbox.max.y - bbox.min.y,
            z: bbox.max.z - bbox.min.z
        };

        const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone();

        if (!vec) return;
        updateLine({
            line: line1.current,
            geometry: lineGeometries.current.posX,
            direction: new Vector3(1, 0, 0),  // Positive X
            angle: 'pos',
            mesh: textPosX,
            vec,
            size
        });
        updateLine({
            line: line2.current,
            geometry: lineGeometries.current.negX,
            direction: new Vector3(-1, 0, 0), // Negative X
            angle: 'neg',
            mesh: textNegX,
            vec,
            size
        });
        updateLine({
            line: line3.current,
            geometry: lineGeometries.current.posZ,
            direction: new Vector3(0, 0, 1),  // Positive Z
            angle: 'pos',
            mesh: textPosZ,
            vec,
            size
        });
        updateLine({
            line: line4.current,
            geometry: lineGeometries.current.negZ,
            direction: new Vector3(0, 0, -1), // Negative Z
            angle: 'neg',
            mesh: textNegZ,
            vec,
            size
        });
        updateLine({
            line: line5.current,
            geometry: lineGeometries.current.posY,
            direction: new Vector3(0, -1, 0), // Down (Y)
            angle: 'posY',
            mesh: textPosY,
            vec,
            size
        });
    });

    const updateLine = ({
        line,
        geometry,
        direction,
        angle,
        mesh,
        vec,
        size
    }: {
        line: Line | null,
        geometry: BufferGeometry,
        direction: Vector3,
        angle: string,
        mesh: React.RefObject<Group>,
        vec: Vector3,
        size: { x: number, y: number, z: number }
    }) => {
        if (!line) return;

        const points = [];

        if (angle === "pos") {
            points[0] = new Vector3(vec.x, vec.y, vec.z).add(
                new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2)
            );
        } else if (angle === "neg") {
            points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
                new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2)
            );
        } else if (angle === "posY") {
            points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
                new Vector3(0, size.y / 2, 0)
            );
        }

        const ray = new Raycaster();
        if (camera) ray.camera = camera;
        ray.set(new Vector3(vec.x, vec.y, vec.z), direction);
        ray.params.Line.threshold = 0.1;

        // Find intersection points
        const wallsGroup = scene.children.find(val => val?.name.includes("Walls"));
        const intersects = wallsGroup ? ray.intersectObjects([wallsGroup], true) : [];

        // Find intersection point
        if (intersects[0]) {
            for (const intersect of intersects) {
                if (intersect.object.name.includes("Wall")) {
                    points[1] = angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor
                    break;
                }
            }
        }

        // Update line geometry
        if (points[1]) {
            geometry.dispose();
            geometry.setFromPoints([points[0], points[1]]);
            line.geometry = geometry;

            // Update measurement text
            if (mesh?.current) {
                geometry.computeBoundingSphere();
                const center = geometry.boundingSphere?.center;
                if (center) {
                    mesh.current.position.copy(center);
                }
                const label = document.getElementById(mesh.current.name);
                if (label) {
                    label.innerText = `${points[0].distanceTo(points[1]).toFixed(2)}m`;
                }
            }
        } else {
            // No intersection found - clear the line
            geometry.dispose();
            geometry.setFromPoints([new Vector3(), new Vector3()]);
            line.geometry = geometry;
            const label = document.getElementById(mesh?.current?.name || "");
            if (label) label.innerText = "";
        }
    };

    const Material = new LineBasicMaterial({ color: "red" });

    return (
        <>
            {/* Measurement text labels */}
            {boundingBoxRef.current && (
                <>
                    <group name="textPosX" ref={textPosX}>
                        <Html zIndexRange={[1, 0]} style={{ pointerEvents: 'none' }}>
                            <div className="distance-label" id="textPosX" style={{
                                background: 'rgba(0,0,0,0.7)',
                                color: 'white',
                                padding: '2px 5px',
                                borderRadius: '3px',
                                fontSize: '12px',
                                whiteSpace: 'nowrap'
                            }}></div>
                        </Html>
                    </group>
                    <group name="textNegX" ref={textNegX}>
                        <Html zIndexRange={[1, 0]} style={{ pointerEvents: 'none' }}>
                            <div className="distance-label" id="textNegX" style={{
                                background: 'rgba(0,0,0,0.7)',
                                color: 'white',
                                padding: '2px 5px',
                                borderRadius: '3px',
                                fontSize: '12px',
                                whiteSpace: 'nowrap'
                            }}></div>
                        </Html>
                    </group>
                    <group name="textPosZ" ref={textPosZ}>
                        <Html zIndexRange={[2, 0]} style={{ pointerEvents: 'none' }}>
                            <div className="distance-label" id="textPosZ" style={{
                                background: 'rgba(0,0,0,0.7)',
                                color: 'white',
                                padding: '2px 5px',
                                borderRadius: '3px',
                                fontSize: '12px',
                                whiteSpace: 'nowrap'
                            }}></div>
                        </Html>
                    </group>
                    <group name="textNegZ" ref={textNegZ}>
                        <Html zIndexRange={[1, 0]} style={{ pointerEvents: 'none' }}>
                            <div className="distance-label" id="textNegZ" style={{
                                background: 'rgba(0,0,0,0.7)',
                                color: 'white',
                                padding: '2px 5px',
                                borderRadius: '3px',
                                fontSize: '12px',
                                whiteSpace: 'nowrap'
                            }}></div>
                        </Html>
                    </group>

                    {/* Measurement lines */}
                    <primitive object={new Line(new BufferGeometry(), Material)} ref={line1} />
                    <primitive object={new Line(new BufferGeometry(), Material)} ref={line2} />
                    <primitive object={new Line(new BufferGeometry(), Material)} ref={line3} />
                    <primitive object={new Line(new BufferGeometry(), Material)} ref={line4} />
                </>
            )}
        </>
    );
};

export default DistanceFindingControls;