import { jsx } from 'react/jsx-runtime';
import { useRef, useEffect } from 'react';
import * as THREE from 'three';
import { cn } from '../../../utils/cn.js';
import { variationMap } from './SpotlightBorder.types.js';
import { fragmentShader, vertexShader } from './SpotlightBorder.shaders.js';

/**
 * SpotlightBorder Component
 * Adds an interactive WebGL border effect around its children.
 * The border illuminates near the mouse cursor and supports a soft outer glow.
 * Refactored to handle larger canvas for external glow and prevent clipping of child shadows.
 *
 * @example
 * ```tsx
 * <SpotlightBorder color="#3b82f6" borderWidth={2} className="rounded-xl">
 *   <div className="p-6 bg-gray-900">
 *     <h3>Premium Card</h3>
 *   </div>
 * </SpotlightBorder>
 * ```
 */
const SpotlightBorder = ({ children, variation = "rounded-rect", rounded, borderWidth = 2, color = "#ffffff", spotlightSize = 0.4, spotlightEdge = 0.5, glowExtent = 20, glowIntensity = 0.6, tiltIntensity = 5, shouldAnimate = true, className, style, ...props }) => {
    var _a;
    const outerRef = useRef(null);
    const containerRef = useRef(null);
    // Check for reduced motion preference
    const prefersReducedMotion = typeof window !== "undefined" &&
        ((_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)").matches);
    const isAnimating = shouldAnimate && !prefersReducedMotion;
    // Refs to store THREE.js objects across effects
    const rendererRef = useRef(null);
    const sceneRef = useRef(null);
    const cameraRef = useRef(null);
    const materialRef = useRef(null);
    const quadRef = useRef(null);
    const geoRef = useRef(null);
    const canvasRef = useRef(null);
    // Refs for animation state
    const vMouseRef = useRef(new THREE.Vector2(-1e4, -1e4));
    const vMouseDampRef = useRef(new THREE.Vector2(-1e4, -1e4));
    const vResolutionRef = useRef(new THREE.Vector2());
    const vContentResolutionRef = useRef(new THREE.Vector2());
    const vOffsetRef = useRef(new THREE.Vector2());
    const rotateXDampRef = useRef(0);
    const rotateYDampRef = useRef(0);
    const wRef = useRef(1);
    const hRef = useRef(1);
    // One-time setup effect: creates renderer, scene, camera, geometry, material, mesh
    useEffect(() => {
        var _a;
        const outer = outerRef.current;
        const container = containerRef.current;
        if (!outer || !container || !isAnimating)
            return;
        const scene = new THREE.Scene();
        const camera = new THREE.OrthographicCamera();
        camera.position.z = 1;
        const renderer = new THREE.WebGLRenderer({ alpha: true });
        renderer.setClearColor(0x000000, 0);
        const canvas = renderer.domElement;
        canvas.style.position = "absolute";
        canvas.style.pointerEvents = "none";
        canvas.style.zIndex = "10";
        outer.appendChild(canvas);
        const geo = new THREE.PlaneGeometry(1, 1);
        const material = new THREE.ShaderMaterial({
            vertexShader,
            fragmentShader,
            uniforms: {
                u_mouse: { value: vMouseDampRef.current },
                u_resolution: { value: vResolutionRef.current },
                u_contentResolution: { value: vContentResolutionRef.current },
                u_offset: { value: vOffsetRef.current },
                u_pixelRatio: { value: Math.min(window.devicePixelRatio || 1, 2) },
                u_color: { value: new THREE.Color(color) },
                u_shapeSize: { value: 1.0 },
                u_roundness: { value: 0.0 },
                u_borderSize: { value: 0.0 },
                u_spotlightSize: { value: spotlightSize },
                u_spotlightEdge: { value: spotlightEdge },
                u_glowIntensity: { value: glowIntensity },
            },
            defines: { VAR: (_a = variationMap[variation]) !== null && _a !== void 0 ? _a : 0 },
            transparent: true,
        });
        const quad = new THREE.Mesh(geo, material);
        scene.add(quad);
        // Store refs
        rendererRef.current = renderer;
        sceneRef.current = scene;
        cameraRef.current = camera;
        materialRef.current = material;
        quadRef.current = quad;
        geoRef.current = geo;
        canvasRef.current = canvas;
        let animationFrameId;
        const onPointerMove = (e) => {
            if (!container)
                return;
            const rect = container.getBoundingClientRect();
            vMouseRef.current.set(e.clientX - rect.left, e.clientY - rect.top);
        };
        document.addEventListener("mousemove", onPointerMove);
        document.addEventListener("pointermove", onPointerMove);
        const borderRadiusPx = parseFloat(getComputedStyle(outer).borderRadius) || 0;
        const resize = () => {
            if (!container || !outer)
                return;
            const w = container.clientWidth;
            const h = container.clientHeight;
            wRef.current = w;
            hRef.current = h;
            const dpr = Math.min(window.devicePixelRatio || 1, 2);
            const canvasW = w + glowExtent * 2;
            const canvasH = h + glowExtent * 2;
            renderer.setSize(canvasW, canvasH);
            renderer.setPixelRatio(dpr);
            canvas.style.top = `-${glowExtent}px`;
            canvas.style.left = `-${glowExtent}px`;
            canvas.style.width = `${canvasW}px`;
            canvas.style.height = `${canvasH}px`;
            camera.left = -canvasW / 2;
            camera.right = canvasW / 2;
            camera.top = canvasH / 2;
            camera.bottom = -canvasH / 2;
            camera.updateProjectionMatrix();
            quad.scale.set(canvasW, canvasH, 1);
            vResolutionRef.current.set(canvasW, canvasH).multiplyScalar(dpr);
            vContentResolutionRef.current.set(w, h).multiplyScalar(dpr);
            vOffsetRef.current.set(glowExtent, glowExtent).multiplyScalar(dpr);
            material.uniforms.u_pixelRatio.value = dpr;
            const shortestDim = Math.min(w, h);
            const childEl = rounded ? null : container.firstElementChild;
            const currentRadius = childEl
                ? parseFloat(getComputedStyle(childEl).borderRadius) || borderRadiusPx
                : parseFloat(getComputedStyle(outer).borderRadius) || borderRadiusPx;
            const normalizedRoundness = Math.min((currentRadius / shortestDim) * 2, 1);
            material.uniforms.u_roundness.value = normalizedRoundness;
            const normalizedBorderWidth = borderWidth / shortestDim;
            material.uniforms.u_borderSize.value = normalizedBorderWidth;
        };
        resize();
        window.addEventListener("resize", resize);
        const ro = new ResizeObserver(() => resize());
        ro.observe(container);
        const animate = (now) => {
            const vMouse = vMouseRef.current;
            const vMouseDamp = vMouseDampRef.current;
            const w = wRef.current;
            const h = hRef.current;
            vMouseDamp.x += (vMouse.x - vMouseDamp.x) * 0.1;
            vMouseDamp.y += (vMouse.y - vMouseDamp.y) * 0.1;
            if (tiltIntensity > 0 && outer && w > 0 && h > 0) {
                const isInside = vMouseDamp.x >= 0 &&
                    vMouseDamp.x <= w &&
                    vMouseDamp.y >= 0 &&
                    vMouseDamp.y <= h;
                let targetRotateX = 0;
                let targetRotateY = 0;
                if (isInside) {
                    const offsetX = vMouseDamp.x / w - 0.5;
                    const offsetY = vMouseDamp.y / h - 0.5;
                    targetRotateY = offsetX * tiltIntensity;
                    targetRotateX = -offsetY * tiltIntensity;
                }
                rotateXDampRef.current += (targetRotateX - rotateXDampRef.current) * 0.08;
                rotateYDampRef.current += (targetRotateY - rotateYDampRef.current) * 0.08;
                outer.style.transform = `perspective(1000px) rotateX(${rotateXDampRef.current}deg) rotateY(${rotateYDampRef.current}deg)`;
            }
            renderer.render(scene, camera);
            animationFrameId = requestAnimationFrame(animate);
        };
        animationFrameId = requestAnimationFrame(animate);
        return () => {
            document.removeEventListener("mousemove", onPointerMove);
            document.removeEventListener("pointermove", onPointerMove);
            window.removeEventListener("resize", resize);
            ro.disconnect();
            cancelAnimationFrame(animationFrameId);
            if (outer.contains(canvas)) {
                outer.removeChild(canvas);
            }
            renderer.dispose();
            geo.dispose();
            material.dispose();
            rendererRef.current = null;
            sceneRef.current = null;
            cameraRef.current = null;
            materialRef.current = null;
            quadRef.current = null;
            geoRef.current = null;
            canvasRef.current = null;
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [isAnimating, variation]);
    // Update shader uniforms when props change (without tearing down WebGL)
    useEffect(() => {
        const material = materialRef.current;
        if (!material)
            return;
        material.uniforms.u_color.value.set(color);
        material.uniforms.u_spotlightSize.value = spotlightSize;
        material.uniforms.u_spotlightEdge.value = spotlightEdge;
        material.uniforms.u_glowIntensity.value = glowIntensity;
    }, [color, spotlightSize, spotlightEdge, glowIntensity]);
    const containerStyle = {
        ...style,
        ...(rounded ? { borderRadius: rounded } : {}),
    };
    return (jsx("div", { ref: outerRef, className: cn("relative overflow-visible", className), style: containerStyle, ...props, children: jsx("div", { ref: containerRef, className: "relative w-full h-full", style: { borderRadius: "inherit", zIndex: 20 }, children: children }) }));
};

export { SpotlightBorder };
