import { useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber';
import { Text } from '@react-three/drei';
import * as THREE from 'three';

interface LODTextProps {
    minDist?: number;  // Distance at which opacity starts fading in (from 0 to 1)
    maxDist?: number;  // Distance at which opacity is 0 (fully transparent)
    fullDist?: number; // Distance at which opacity is 1 (fully opaque)
    // Pass-through props for Text
    [key: string]: any;
}

/**
 * Text component that handles Level of Detail (LOD) by fading opacity based on camera distance.
 */
export function LODText({
    minDist = 0,
    maxDist = 100,
    fullDist = 20,
    color = '#ffffff',
    fillOpacity = 1,
    children,
    ...props
}: LODTextProps) {
    const materialRef = useRef<THREE.MeshBasicMaterial>(null);
    const textRef = useRef<any>(null); // Type 'any' because drei Text ref typing can be tricky

    // Store base opacity to multiply with LOD factor
    const baseOpacity = typeof fillOpacity === 'number' ? fillOpacity : 1;

    useFrame(({ camera }) => {
        if (!textRef.current) return;

        // Get world position of the text object
        // Note: This might be expensive if done every frame for thousands of objects.
        // Optimization: only update every X frames or if camera moved significantly?
        // For now, let's trust React Three Fiber's loop to be efficient enough for ~50-100 labels.

        // Calculate distance
        const dist = camera.position.distanceTo(textRef.current.position);

        let opacity = 0;

        if (dist > maxDist) {
            opacity = 0;
        } else if (dist <= fullDist) {
            opacity = 1;
        } else {
            // Linear fade between fullDist and maxDist
            opacity = 1 - (dist - fullDist) / (maxDist - fullDist);
        }

        // Apply
        if (materialRef.current) {
            materialRef.current.opacity = opacity * baseOpacity;
            materialRef.current.transparent = true;

            // Optimization: Set visible=false if opacity is near zero to skip render
            textRef.current.visible = opacity > 0.05;
        }

        // Also update outline opacity if it exists (drei Text handles outlines via different meshes internally, 
        // so direct material manipulation primarily affects the fill. 
        // We'll rely on the 'fillOpacity' prop if we were re-rendering, but we're mutating material directly.)
        // Actually, drei Text exposes fillOpacity which we can set on the component.
        // But adjusting props triggers React re-render which is deadly in useFrame.
        // We MUST mutate the material opacity directly.
    });

    return (
        <Text
            ref={textRef}
            fillOpacity={baseOpacity} // Initial prop, overridden by useFrame mutation
            color={color}
            // We need to ensure the material is accessible. 
            // Drei Text creates its own material. We can attach a callback or just access it via ref.
            onSync={(textMesh: any) => {
                // Ensure material is transparent
                if (textMesh.material) {
                    textMesh.material.transparent = true;
                    // @ts-ignore - Valid runtime assignment for refs
                    materialRef.current = textMesh.material;
                }
            }}
            {...props}
        >
            {children}
        </Text>
    );
}
