import cloud from 'd3-cloud';
import { useEffect, useRef, useState } from 'react';

export type WordCloudItem = { text: string; value: number };

type CloudWord = cloud.Word & {
    text: string;
    size: number;
    value?: number;
};

const DEFAULT_HEIGHT = 420;

function hashString(input: string): number {
    let h = 2166136261;
    for (let i = 0; i < input.length; i += 1) {
        h ^= input.charCodeAt(i);
        h = Math.imul(h, 16777619);
    }
    return h >>> 0;
}

/** Color pastel estable por palabra (no cambia entre renders). */
function pastelFill(text: string, index: number): string {
    const seed = `${text}\u0000${index}`;
    const h1 = hashString(seed);
    const h2 = hashString(text);
    const hue = h1 % 360;
    const sat = 38 + (h2 % 22);
    const light = 74 + ((h1 >> 9) % 14);
    return `hsl(${hue} ${sat}% ${light}%)`;
}

export default function D3WordCloud({
    items,
    svgClassName,
    onWordClick,
}: {
    items: WordCloudItem[];
    svgClassName?: string;
    /** Al hacer clic en una palabra (p. ej. abrir coincidencias en transcripciones). */
    onWordClick?: (text: string) => void;
}) {
    const containerRef = useRef<HTMLDivElement>(null);
    const [size, setSize] = useState({ width: 560, height: DEFAULT_HEIGHT });
    const [placed, setPlaced] = useState<CloudWord[]>([]);

    useEffect(() => {
        const el = containerRef.current;
        if (!el) {
            return;
        }
        const measure = () => {
            const r = el.getBoundingClientRect();
            const w = Math.max(200, Math.floor(r.width));
            const h = Math.max(200, Math.floor(r.height));
            setSize({ width: w, height: h });
        };
        measure();
        const ro = new ResizeObserver(measure);
        ro.observe(el);
        return () => ro.disconnect();
    }, []);

    useEffect(() => {
        if (!items.length) {
            setPlaced([]);
            return;
        }

        let cancelled = false;

        const values = items.map((d) => d.value);
        const maxV = Math.max(...values);
        const minV = Math.min(...values);
        const fontSize = (v: number) =>
            maxV === minV ? 28 : 14 + ((v - minV) / (maxV - minV)) * 46;

        const words: CloudWord[] = items.map((d) => ({
            text: d.text,
            size: fontSize(d.value),
            value: d.value,
        }));

        const layout = cloud<CloudWord>()
            .size([size.width, size.height])
            .words(words)
            .padding(2)
            .rotate(() => (Math.random() - 0.5) * 36)
            .font('system-ui, "Segoe UI", Roboto, sans-serif')
            .fontSize((d) => d.size)
            .spiral('archimedean')
            .on('end', (out) => {
                if (!cancelled) {
                    setPlaced(out);
                }
            });

        layout.start();

        return () => {
            cancelled = true;
        };
    }, [items, size.width, size.height]);

    return (
        <div
            ref={containerRef}
            style={{
                width: '100%',
                height: '100%',
                minHeight: 0,
                minWidth: 0,
            }}
        >
            {items.length === 0 ? null : (
                <svg
                    className={svgClassName}
                    width={size.width}
                    height={size.height}
                    role="img"
                    style={{ display: 'block', maxWidth: '100%', height: 'auto' }}
                >
                    <g
                        transform={`translate(${size.width / 2},${size.height / 2})`}
                    >
                        {placed.map((w, i) => (
                            <text
                                key={`${w.text}-${i}`}
                                textAnchor="middle"
                                transform={`translate(${w.x},${w.y})rotate(${w.rotate ?? 0})`}
                                fontSize={w.size}
                                fill={pastelFill(w.text, i)}
                                style={{
                                    fontWeight: 600,
                                    cursor: onWordClick ? 'pointer' : undefined,
                                }}
                                role={onWordClick ? 'button' : undefined}
                                tabIndex={onWordClick ? 0 : undefined}
                                onClick={
                                    onWordClick
                                        ? () => {
                                              onWordClick(w.text);
                                          }
                                        : undefined
                                }
                                onKeyDown={
                                    onWordClick
                                        ? (e) => {
                                              if (
                                                  e.key === 'Enter' ||
                                                  e.key === ' '
                                              ) {
                                                  e.preventDefault();
                                                  onWordClick(w.text);
                                              }
                                          }
                                        : undefined
                                }
                            >
                                {w.text}
                            </text>
                        ))}
                    </g>
                </svg>
            )}
        </div>
    );
}
