import * as THREE from "three";
import { useEffect } from "react";
import * as turf from "@turf/turf";
import * as Types from "../../../../types/world/worldTypes";
import { useThree } from "@react-three/fiber";
import { useSceneContext } from "../../../scene/sceneContext";

interface PolygonGeneratorProps {
    groupRef: React.MutableRefObject<THREE.Group | null>;
}

export default function PolygonGenerator({
    groupRef,
}: PolygonGeneratorProps) {
    const { aisleStore, wallStore } = useSceneContext();
    const { aisles } = aisleStore();
    const { scene } = useThree();
    const { walls } = wallStore();

    useEffect(() => {
        const result = aisles
            .filter(
                (aisle) =>
                    aisle.type.aisleType === "dotted-aisle" ||
                    aisle.type.aisleType === "solid-aisle" ||
                    aisle.type.aisleType === "dashed-aisle" ||
                    aisle.type.aisleType === "arc-aisle"
            )
            .map((aisle) =>
                aisle.points.map((point) => ({
                    position: [point.position[0], point.position[2]],
                    uuid: point.pointUuid,
                }))
            );

        const arcAndCircleResult = aisles
            .filter(
                (aisle) =>
                    aisle.type.aisleType === "circle-aisle" ||
                    aisle.type.aisleType === "arc-aisle"
            )

        arcAndCircleResult.forEach((arc) => {
            const arcGroup = scene.getObjectByProperty("uuid", arc.aisleUuid);
            if (!arcGroup) return;
            const cloned = arcGroup?.clone();
            cloned.position.set(cloned.position.x, cloned.position.y + 5, cloned.position.z);
            cloned?.traverse((child) => {
                if (child instanceof THREE.Mesh && child.geometry instanceof THREE.ExtrudeGeometry) {
                    const extrudeGeometry = child.geometry as THREE.ExtrudeGeometry;
                    extrudeGeometry.scale(1, 1, 500);
                    child.geometry = extrudeGeometry;
                    child.position.set(cloned.position.x, cloned.position.y + 0.1, cloned.position.z)
                    child.rotateX(Math.PI / 2);
                    child.name = "agv-arc-collider"
                    groupRef.current?.add(child)
                }

            });
        });

        const wallPoints: THREE.Vector3[][] = walls
            .map((wall) =>
                wall.points.map((pt) => new THREE.Vector3(pt.position[0], pt.position[1], pt.position[2]))
            )
            .filter(points => points.length === 2);


        if (!result || result.some((line) => !line)) {
            return;
        }

        const lineFeatures = result?.map((line: any) =>
            turf.lineString(line.map((p: any) => p?.position))
        );

        let validLineFeatures = lineFeatures.filter((line) => {
            const coords = line.geometry.coordinates;
            return coords.length >= 2;
        }).filter((line) => {
            if (line.geometry.coordinates[0].toString() !== line.geometry.coordinates[1].toString()) {
                return true;
            }
        })

        if (validLineFeatures.length < 3) { validLineFeatures = [] }

        const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));

        renderWallGeometry(wallPoints);

        if (polygons.features.length > 0) {
            polygons.features.forEach((feature) => {
                if (feature.geometry.type === "Polygon") {

                    const shape = new THREE.Shape();
                    const coords = feature.geometry.coordinates[0];

                    shape.moveTo(coords[0][0], coords[0][1]);

                    for (let i = 1; i < coords.length; i++) {
                        shape.lineTo(coords[i][0], coords[i][1]);
                    }
                    shape.lineTo(coords[0][0], coords[0][1]);

                    const extrudeSettings = {
                        depth: 5,
                        bevelEnabled: false,
                    };

                    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);

                    const material = new THREE.MeshBasicMaterial({ color: "blue", transparent: true, opacity: 0.5 });
                    const mesh = new THREE.Mesh(geometry, material);
                    mesh.rotateX(Math.PI / 2);
                    mesh.name = "agv-collider";
                    mesh.position.y = 5;

                    mesh.receiveShadow = true;
                    groupRef.current?.add(mesh);
                }
            });
        }

    }, [aisles, scene, walls]);

    const renderWallGeometry = (walls: THREE.Vector3[][]) => {
        walls.forEach((wall) => {
            if (wall.length < 2) return;

            for (let i = 0; i < wall.length - 1; i++) {
                const start = new THREE.Vector3(wall[i].x, wall[i].y, wall[i].z);
                const end = new THREE.Vector3(
                    wall[i + 1].x,
                    wall[i + 1].y,
                    wall[i + 1].z
                );

                const wallHeight = 10;
                const direction = new THREE.Vector3().subVectors(end, start);
                const length = direction.length();
                direction.normalize();

                const wallGeometry = new THREE.BoxGeometry(length, wallHeight);
                const wallMaterial = new THREE.MeshBasicMaterial({
                    color: "#aaa",
                    transparent: true,
                    opacity: 0.5,
                });

                const wallMesh = new THREE.Mesh(wallGeometry, wallMaterial);
                const midPoint = new THREE.Vector3()
                    .addVectors(start, end)
                    .multiplyScalar(0.5);
                wallMesh.position.set(midPoint.x, wallHeight / 2, midPoint.z);

                const quaternion = new THREE.Quaternion();
                quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction);
                wallMesh.quaternion.copy(quaternion);
                wallMesh.name = "agv-collider";

                groupRef.current?.add(wallMesh);
            }
        });
    };

    return null;
}
