import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { useThree, useFrame } from '@react-three/fiber';
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore';
import { CameraControls } from '@react-three/drei';
import { useSceneContext } from '../../../scene/sceneContext';

const collisionWorker = new Worker(new URL('../../../../services/simulation/webWorkers/collisionWorker.ts', import.meta.url));

export default function MaterialCollisionDetector() {
    const { materialStore } = useSceneContext();
    const { materials } = materialStore();
    const { scene, controls, camera } = useThree();
    const positionsRef = useRef<Record<string, THREE.Vector3>>({});
    const sizesRef = useRef<Record<string, number>>({});
    const { isPlaying } = usePlayButtonStore();
    const { isPaused, setIsPaused } = usePauseButtonStore();
    const { isReset } = useResetButtonStore();
    const collisionStateRef = useRef<Record<string, boolean>>({});

    useEffect(() => {
        if (isReset) {
            collisionStateRef.current = {};
        }
    }, [isReset])

    useEffect(() => {
        collisionWorker.onmessage = (e) => {
            const { collisions } = e.data;
            handleCollisions(collisions);
        };
    }, []);

    useFrame(() => {
        if (materials.length < 2 || !isPlaying || isPaused) return;

        const { positions, sizes } = getCurrentMaterialData();
        positionsRef.current = positions;
        sizesRef.current = sizes;

        collisionWorker.postMessage({
            materials: materials,
            positions: positions,
            sizes: sizes
        });
    });

    const getCurrentMaterialData = () => {
        const positions: Record<string, THREE.Vector3> = {};
        const sizes: Record<string, number> = {};

        materials.forEach(material => {
            if (material.isVisible) {
                const obj = scene.getObjectByProperty('uuid', material.materialId);
                if (obj) {
                    const position = new THREE.Vector3();
                    obj.getWorldPosition(position);
                    positions[material.materialId] = position;

                    const boundingBox = new THREE.Box3().setFromObject(obj);
                    const sizeVector = new THREE.Vector3();
                    boundingBox.getSize(sizeVector);
                    sizes[material.materialId] = Math.max(sizeVector.x, sizeVector.y, sizeVector.z) / 2;
                }
            }
        });

        return { positions, sizes };
    };

    const handleCollisions = (currentCollisions: Array<{
        materialId1: string;
        materialId2: string;
        distance: number;
    }>) => {
        const newCollisionState: Record<string, boolean> = {};

        currentCollisions.forEach(collision => {
            const key = `${collision.materialId1}|-|${collision.materialId2}`;
            newCollisionState[key] = true;
        });

        Object.keys(collisionStateRef.current).forEach(key => {
            if (!newCollisionState[key]) {
                const [id1, id2] = key.split('|-|');
                echo.error(`Collision ended between ${id1} and ${id2}`);
            }
        });

        currentCollisions.forEach(collision => {
            const key = `${collision.materialId1}|-|${collision.materialId2}`;
            if (!collisionStateRef.current[key]) {
                setIsPaused(true);
                echo.error(`Collision started between ${collision.materialId1} and ${collision.materialId2}`);
                const obj = scene.getObjectByProperty('uuid', collision.materialId1)
                if (obj) {
                    const collisionPos = new THREE.Vector3();
                    obj.getWorldPosition(collisionPos);

                    const currentPos = new THREE.Vector3().copy(camera.position);

                    const target = new THREE.Vector3();
                    if (!controls) return;
                    (controls as CameraControls).getTarget(target);
                    const direction = new THREE.Vector3().subVectors(target, currentPos).normalize();

                    const offsetDistance = 5;
                    const newCameraPos = new THREE.Vector3().copy(collisionPos).sub(direction.multiplyScalar(offsetDistance));

                    camera.position.copy(newCameraPos);
                    (controls as CameraControls).setLookAt(newCameraPos.x, newCameraPos.y, newCameraPos.z, collisionPos.x, collisionPos.y, collisionPos.z, true);

                }
            }
        });

        collisionStateRef.current = newCollisionState;
    };

    return null;
}