import { useEffect, useRef, useState } from "react";
import { useThree, useFrame } from "@react-three/fiber";
import { Group, Vector3 } from "three";
import { CameraControls } from '@react-three/drei';
import { useLimitDistance, useRenderDistance, useSelectedFloorItem, useToggleView } from '../../../../store/builder/store';
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
import { useSceneContext } from '../../../scene/sceneContext';

import Model from './model/model';
import { GLTFLoader } from "three/examples/jsm/Addons";

const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url));

function Models({ loader }: { loader: GLTFLoader }) {
    const { controls, camera } = useThree();
    const assetGroupRef = useRef<Group>(null);
    const { assetStore } = useSceneContext();
    const { assets } = assetStore();
    const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
    const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
    const { limitDistance } = useLimitDistance();
    const { renderDistance } = useRenderDistance();

    const [renderMap, setRenderMap] = useState<Record<string, boolean>>({});

    const cameraPos = useRef(new Vector3());

    useEffect(() => {
        distanceWorker.onmessage = (e) => {
            const { shouldRender, modelUuid } = e.data;
            setRenderMap((prev) => {
                if (prev[modelUuid] === shouldRender) return prev;
                return { ...prev, [modelUuid]: shouldRender };
            });
        };
    }, []);

    useFrame(() => {
        camera.getWorldPosition(cameraPos.current);
        for (const asset of assets) {
            const isRendered = renderMap[asset.modelUuid] ?? false;
            distanceWorker.postMessage({ modelUuid: asset.modelUuid, assetPosition: { x: asset.position[0], y: asset.position[1], z: asset.position[2], }, cameraPosition: cameraPos.current, limitDistance, renderDistance, isRendered, });
        }
    });

    return (
        <group
            name='Asset Group'
            ref={assetGroupRef}
            onPointerMissed={(e) => {
                e.stopPropagation();
                if (selectedFloorItem) {
                    const target = (controls as CameraControls).getTarget(new Vector3());
                    (controls as CameraControls).setTarget(target.x, 0, target.z, true);
                    setSelectedFloorItem(null);
                }
                if (selectedAsset) {
                    clearSelectedAsset();
                }
            }}
        >
            {assets.map((asset) => (
                <Model key={asset.modelUuid} asset={asset} isRendered={renderMap[asset.modelUuid] ?? false} loader={loader} />
            ))}
        </group>
    );
}

export default Models;