import { useEffect, useMemo, useRef, useState } from "react";
import { Html } from "@react-three/drei";
import * as THREE from "three";
import {
    useToggleView,
    useZonePoints,
} from "../../../../store/builder/store";
import { getZonesApi } from "../../../../services/factoryBuilder/zones/getZonesApi";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import * as turf from "@turf/turf";
import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore";

const ZoneDuplicate = ({ projectId }: { projectId: string }) => {
    const [zones, setZones] = useState([]);
    const { setZonePoints } = useZonePoints();
    const { selectedZone } = useSelectedZoneStore();
    const { toggleView } = useToggleView();

    const groupsRef = useRef<any>();

    const zoneMaterial = useMemo(
        () =>
            new THREE.ShaderMaterial({
                side: THREE.DoubleSide,
                vertexShader: `
        varying vec2 vUv;
        void main(){
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            vUv = uv;
        }
        `,
                fragmentShader: `
        varying vec2 vUv;
        uniform vec3 uOuterColor;
        void main(){
            float alpha = 1.0 - vUv.y;
            gl_FragColor = vec4(uOuterColor, alpha);
        }
        `,
                uniforms: {
                    uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) },
                },
                transparent: true,
                depthWrite: false,
            }),
        []
    );

    useEffect(() => {
        const fetchZones = async () => {
            const email = localStorage.getItem("email");
            if (!email) return;

            const organization = email.split("@")[1].split(".")[0];
            const data = await getZonesApi(organization, projectId);

            if (data.length > 0) {
                const fetchedZones = data.map((zone: any) => ({
                    zoneUuid: zone.zoneUuid,
                    zoneName: zone.zoneName,
                    points: zone.points,
                    viewPortCenter: zone.viewPortCenter,
                    viewPortposition: zone.viewPortposition,
                    layer: zone.layer,
                }));

                setZones(fetchedZones);

                const fetchedPoints = data.flatMap((zone: any) =>
                    zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point))
                );

                setZonePoints(fetchedPoints);
            }
        };

        fetchZones();
    }, []);

    return (
        <group ref={groupsRef} name="zoneGroup">
            <group name="zones" visible={!toggleView}>
                {zones.map((zone: any) => (
                    <group
                        key={zone.zoneUuid}
                        name={zone.zoneName}
                        visible={zone.zoneUuid === selectedZone.zoneUuid}
                    >
                        {zone.points
                            .slice(0, -1)
                            .map((point: [number, number, number], index: number) => {
                                const nextPoint = zone.points[index + 1];

                                const point1 = new THREE.Vector3(point[0], point[1], point[2]);
                                const point2 = new THREE.Vector3(
                                    nextPoint[0],
                                    nextPoint[1],
                                    nextPoint[2]
                                );

                                const planeWidth = point1.distanceTo(point2);
                                const planeHeight = CONSTANTS.zoneConfig.height;

                                const midpoint = new THREE.Vector3(
                                    (point1.x + point2.x) / 2,
                                    CONSTANTS.zoneConfig.height / 2 +
                                    (zone.layer - 1) * CONSTANTS.zoneConfig.height,
                                    (point1.z + point2.z) / 2
                                );

                                const angle = Math.atan2(
                                    point2.z - point1.z,
                                    point2.x - point1.x
                                );

                                return (
                                    <mesh
                                        key={index}
                                        position={midpoint}
                                        rotation={[0, -angle, 0]}
                                    >
                                        <planeGeometry args={[planeWidth, planeHeight]} />
                                        <primitive
                                            object={zoneMaterial.clone()}
                                            attach="material"
                                        />
                                    </mesh>
                                );
                            })}
                        {!toggleView &&
                            (() => {
                                const points3D = zone.points || [];
                                const coords2D = points3D.map((p: any) => [p[0], p[2]]);

                                // Ensure the polygon is closed
                                if (
                                    coords2D.length >= 4 &&
                                    (coords2D[0][0] !== coords2D[coords2D.length - 1][0] ||
                                        coords2D[0][1] !== coords2D[coords2D.length - 1][1])
                                ) {
                                    coords2D.push(coords2D[0]);
                                }
                                if (coords2D.length < 4) return null;

                                const polygon = turf.polygon([coords2D]);
                                const center2D = turf.center(polygon).geometry.coordinates;

                                // Calculate the average Y value
                                const sumY = points3D.reduce(
                                    (sum: number, p: any) => sum + p[1],
                                    0
                                );
                                const avgY = points3D.length > 0 ? sumY / points3D.length : 0;

                                const htmlPosition: [number, number, number] = [
                                    center2D[0],
                                    avgY + (CONSTANTS.zoneConfig.height || 0) + 1.5,
                                    center2D[1],
                                ];

                                return (
                                    <Html
                                        // data
                                        key={zone.zoneUuid}
                                        position={htmlPosition}
                                        // class
                                        className="zone-name-wrapper"
                                        // others
                                        center
                                    >
                                        <div className="zone-name">{zone.zoneName}</div>
                                    </Html>
                                );
                            })()}
                    </group>
                ))}
            </group>
        </group>
    );
};

export default ZoneDuplicate;
