import React, { useEffect, useRef, useState } from "react";

export interface Tag {
    src: string;
    size?: number;
    phi?: number;
    theta?: number;
    x?: number;
    y?: number;
    z?: number;
    img?: HTMLImageElement;
}

export type CloudMode = 'sphere' | 'horizontal';

interface GlobeTagCloudProps {
    tags: Tag[];
    height: number;
    width: number;
    rotationSpeed?: number;
    enablePulse?: boolean;
    mode?: CloudMode;
}

const TagCloudCanvas: React.FC<GlobeTagCloudProps> = ({
    tags,
    height,
    width,
    rotationSpeed = 0.5,
    enablePulse = false,
    mode = 'sphere'
}) => {
    const canvasRef = useRef<HTMLCanvasElement>(null);
    // initializedTags stores the *current* position state of the tags
    const [initializedTags, setInitializedTags] = useState<Tag[]>([]);
    const [imagesLoaded, setImagesLoaded] = useState(false);
    const animationRef = useRef<number>(0);

    // Use a ref to store positions for the animation loop to modify directly
    // This avoids React state updates on every frame which would be too slow
    // x, y, z are on the unit sphere [-1, 1]
    const positionsRef = useRef<Array<{ x: number; y: number; z: number; scale: number; tag: Tag }>>([]);

    const rotationRef = useRef({
        // Normalize initial axis (-1, 1) -> (-0.707, 0.707)
        x: -0.7071,
        y: 0.7071,
    });

    useEffect(() => {
        const distributeOnGlobe = (index: number, total: number) => {
            const goldenRatio = (1 + Math.sqrt(5)) / 2;
            const i = index + 0.5;
            const phi = Math.acos(1 - 2 * i / total);
            const theta = 2 * Math.PI * i / goldenRatio;

            return {
                x: Math.cos(theta) * Math.sin(phi),
                y: Math.sin(theta) * Math.sin(phi),
                z: Math.cos(phi)
            };
        };

        const distributeOnCylinder = (index: number, total: number) => {
            // Distribute evenly around the Y-axis
            const theta = (2 * Math.PI * index) / total;
            // Distribute Y from -1 to 1 based on index (or random)
            // Using index based distribution to cover height evenly
            // We want to avoid all tags being on a single line if few tags
            // Let's use a staggered grid or just linear distribution for now
            // Or maybe randomize Y slightly or use golden ratio for Y too?
            // Simple linear spread for Y might be boring.
            // Let's use golden ratio for Y to avoid stacking.

            // Map index to [-0.8, 0.8] to avoid poles being too crowded or cut off
            // Actually for cylinder, top and bottom are fine.
            const y = 1 - (index / (total - 1)) * 2;

            return {
                x: Math.sin(theta),
                y: y,
                z: Math.cos(theta)
            };
        };

        const loadImages = async () => {
            try {
                const loadedTags = await Promise.all(
                    tags.map(async (tag, index) => {
                        const img = new Image();
                        img.src = tag.src;
                        await new Promise((resolve, reject) => {
                            img.onload = resolve;
                            img.onerror = reject;
                        });

                        // Initial distribution based on mode
                        let pos;
                        if (mode === 'horizontal') {
                            pos = distributeOnCylinder(index, tags.length);
                        } else {
                            pos = distributeOnGlobe(index, tags.length);
                        }

                        const baseSize = Math.min(width, height) * 0.08;

                        return {
                            ...tag,
                            img,
                            x: pos.x,
                            y: pos.y,
                            z: pos.z,
                            size: tag.size ?? baseSize
                        };
                    })
                );

                // Initialize positionsRef
                positionsRef.current = loadedTags.map(tag => ({
                    tag,
                    x: tag.x!,
                    y: tag.y!,
                    z: tag.z!,
                    scale: 1
                }));

                setInitializedTags(loadedTags);
                setImagesLoaded(true);
            } catch (error) {
                console.error("Error loading images:", error);
            }
        };

        loadImages();
    }, [tags, width, height, mode]);

    useEffect(() => {
        if (!imagesLoaded || !canvasRef.current || positionsRef.current.length === 0) return;

        const canvas = canvasRef.current;
        const ctx = canvas.getContext("2d");
        if (!ctx) return;

        const radius = Math.min(width, height) * 0.35;

        const handleMouseMove = (e: MouseEvent) => {
            if (mode === 'horizontal') return; // Disable mouse rotation for horizontal mode for now

            const rect = canvas.getBoundingClientRect();
            const centerX = rect.left + width / 2;
            const centerY = rect.top + height / 2;

            const dx = e.clientX - centerX;
            const dy = e.clientY - centerY;
            const distance = Math.sqrt(dx * dx + dy * dy);

            if (distance > 0) {
                // Determine rotation axis from mouse position
                rotationRef.current.x = dy / distance;
                rotationRef.current.y = dx / distance;
            }
        };

        const handleMouseLeave = () => {
            if (enablePulse && mode === 'sphere') {
                // Reset to diagonal, normalized
                rotationRef.current.x = -0.7071;
                rotationRef.current.y = 0.7071;
            }
        };

        const drawTag = (
            ctx: CanvasRenderingContext2D,
            tag: Tag,
            x: number,
            y: number,
            scale: number
        ) => {
            if (!tag.img) return;

            ctx.save();
            ctx.translate(x, y);

            const size = tag.size! * (0.6 + scale * 0.4);
            const opacity = Math.pow(scale, 1.5);
            ctx.globalAlpha = Math.min(1, Math.max(0.2, opacity));

            ctx.drawImage(
                tag.img,
                -size / 2,
                -size / 2,
                size,
                size
            );
            ctx.restore();
        };

        const animate = () => {
            ctx.clearRect(0, 0, width, height);
            ctx.save();
            ctx.translate(width / 2, height / 2);

            // Incremental rotation angle
            // For horizontal mode, we might want constant speed regardless of mouse?
            // Or just use rotationSpeed prop.
            const angle = rotationSpeed * 0.01;
            const cos = Math.cos(angle);
            const sin = Math.sin(angle);

            if (mode === 'horizontal') {
                // Force rotation around Y axis
                // Axis vector (0, 1, 0)
                // However, our rotation logic rotates around (rx, ry, 0) - this is a limitation of the current math!
                // The current math assumes rotation axis is in XY plane (perpendicular to Z).

                // Wait, the previous implementation:
                // rotationRef.current.x corresponds to logical X component of axis?
                // Let's re-read the math.

                // Current math:
                // const newX = (cos + rx * rx * (1 - cos)) * x + (rx * ry * (1 - cos)) * y + (ry * sin) * z;
                // This is Rodrigues formula for axis (rx, ry, 0) IF z-component of axis is 0.

                // To rotate around Y axis (0, 1, 0), we need a general rotation matrix or update the formula.
                // Standard Y-axis rotation:
                // x' = x cos(a) + z sin(a)
                // y' = y
                // z' = -x sin(a) + z cos(a)

                // We should branch logic here.

                positionsRef.current.forEach(p => {
                    const { x, y, z } = p;

                    const newX = x * cos + z * sin;
                    const newY = y;
                    const newZ = -x * sin + z * cos;

                    p.x = newX;
                    p.y = newY;
                    p.z = newZ;

                    // Scale logic relies on Z depth
                    p.scale = (radius + newZ * radius) / (radius * 2);
                });

            } else {
                // Sphere logic
                const rx = rotationRef.current.x;
                const ry = rotationRef.current.y;

                positionsRef.current.forEach(p => {
                    const { x, y, z } = p;

                    // Rotate point around current axis (rx, ry, 0)
                    const newX = (cos + rx * rx * (1 - cos)) * x + (rx * ry * (1 - cos)) * y + (ry * sin) * z;
                    const newY = (rx * ry * (1 - cos)) * x + (cos + ry * ry * (1 - cos)) * y - (rx * sin) * z;
                    const newZ = (-ry * sin) * x + (rx * sin) * y + cos * z;

                    p.x = newX;
                    p.y = newY;
                    p.z = newZ;

                    p.scale = (radius + newZ * radius) / (radius * 2);
                });
            }

            // Sort by Z for proper occlusion
            const sortedPositions = [...positionsRef.current].sort((a, b) => b.z - a.z);

            sortedPositions.forEach(p => {
                drawTag(ctx, p.tag, p.x * radius, p.y * radius, p.scale);
            });

            ctx.restore();
            animationRef.current = requestAnimationFrame(animate);
        };

        canvas.addEventListener("mousemove", handleMouseMove);
        canvas.addEventListener("mouseleave", handleMouseLeave);

        animationRef.current = requestAnimationFrame(animate);

        return () => {
            if (animationRef.current) {
                cancelAnimationFrame(animationRef.current);
            }
            canvas.removeEventListener("mousemove", handleMouseMove);
            canvas.removeEventListener("mouseleave", handleMouseLeave);
        };
    }, [imagesLoaded, height, width, rotationSpeed, enablePulse, mode]);

    return (
        <div className="relative w-full h-full flex items-center justify-center">
            <canvas
                ref={canvasRef}
                width={width}
                height={height}
                className="bg-transparent"
            />
        </div>
    );
};

export default TagCloudCanvas;