import { jsx, jsxs } from "react/jsx-runtime";
import {
  useMemo,
  useState,
  useCallback,
  useEffect,
  useRef
} from "react";
import { tokenNameToCssVar } from "./utils/token-resolver.js";
import FloatingTooltip from "@/components/charts/native/FloatingTooltip";
const THEME_COLORS = [
  "var(--semi-color-primary)",
  "var(--semi-color-info)",
  "var(--semi-color-primary-light-active)"
];
const WORD_CLOUD_FONT_SIZES = [16, 50];
const WORD_CLOUD_CATEGORY_FILLS = [
  "var(--semi-color-primary)",
  "var(--semi-color-danger)",
  "var(--semi-color-text-2)",
  "var(--semi-color-info)",
  "var(--semi-color-primary-light-active)"
];
const DEFAULT_CATEGORY_LABELS = ["\u5206\u7C7B 1", "\u5206\u7C7B 2", "\u5206\u7C7B 3", "\u5206\u7C7B 4", "\u5206\u7C7B 5"];
const TONE_FILL = {
  positive: "var(--semi-color-primary)",
  neutral: "var(--semi-color-text-2)",
  negative: "var(--semi-color-danger)"
};
const DEFAULT_HEIGHT_PX = 300;
function escapeHtml(s) {
  return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function simpleHash(s) {
  let h = 0;
  for (let i = 0; i < s.length; i += 1) h = (h << 5) - h + s.charCodeAt(i);
  return Math.abs(h);
}
function clampCategoryCount(n) {
  if (!Number.isFinite(n)) return 0;
  return Math.min(5, Math.max(0, Math.floor(n)));
}
function formatInteger(n) {
  return Math.round(n).toLocaleString("zh-CN");
}
function normalizeWords(list) {
  return (Array.isArray(list) ? list : []).map((d) => ({
    text: String(d.text ?? ""),
    value: Number(d.value) || 0,
    ...typeof d.category === "number" && Number.isFinite(d.category) ? { category: Math.floor(d.category) } : {},
    ...d.tone ? { tone: d.tone } : {},
    ...d.tooltipLines?.length ? { tooltipLines: d.tooltipLines } : {}
  }));
}
function wordFill(word, slots, dataTokens) {
  if (typeof word.category === "number" && Number.isFinite(word.category) && slots > 0 && dataTokens?.length === slots) {
    const idx2 = (word.category % slots + slots) % slots;
    const raw = dataTokens[idx2];
    const cssVar = raw ? tokenNameToCssVar(raw) : "";
    if (cssVar) return `var(${cssVar})`;
  }
  if (typeof word.category === "number" && Number.isFinite(word.category) && slots > 0) {
    const idx2 = (word.category % slots + slots) % slots;
    return WORD_CLOUD_CATEGORY_FILLS[idx2 % WORD_CLOUD_CATEGORY_FILLS.length];
  }
  const t = word.tone;
  if (t && TONE_FILL[t]) return TONE_FILL[t];
  const idx = simpleHash(word.text) % THEME_COLORS.length;
  return THEME_COLORS[idx];
}
function wordTooltipHtml(word, legendSlots, labels, dataTokens) {
  if (word.tooltipLines?.length) {
    return word.tooltipLines.map((l) => `${escapeHtml(l.label)}\uFF1A${escapeHtml(l.value)}`).join("<br/>");
  }
  const dotIdx = typeof word.category === "number" && Number.isFinite(word.category) && legendSlots > 0 ? (word.category % legendSlots + legendSlots) % legendSlots : null;
  let dotAttrs = 'class="smart-wordcloud__dot" aria-hidden="true"';
  if (dotIdx != null && dataTokens?.length === legendSlots && dataTokens[dotIdx] && tokenNameToCssVar(dataTokens[dotIdx])) {
    const v = tokenNameToCssVar(dataTokens[dotIdx]);
    dotAttrs = `class="smart-wordcloud__dot" style="background:var(${v})" aria-hidden="true"`;
  } else if (dotIdx != null) {
    dotAttrs = `class="smart-wordcloud__dot smart-wordcloud__dot--cat${dotIdx % WORD_CLOUD_CATEGORY_FILLS.length}" aria-hidden="true"`;
  } else {
    dotAttrs = 'class="smart-wordcloud__dot smart-wordcloud__dot--cat0" aria-hidden="true"';
  }
  const typeLabel = dotIdx != null && labels[dotIdx] ? `<div class="smart-wordcloud-popover__meta">${escapeHtml(labels[dotIdx] ?? "")}</div>` : "";
  return [
    '<div class="smart-wordcloud-popover">',
    '<div class="smart-wordcloud-popover__row">',
    '<span class="smart-wordcloud-popover__left">',
    `<span ${dotAttrs}></span>`,
    `<span class="smart-wordcloud-popover__title">${escapeHtml(word.text)}</span>`,
    "</span>",
    `<span class="smart-wordcloud-popover__value">${escapeHtml(formatInteger(word.value))}</span>`,
    "</div>",
    typeLabel,
    "</div>"
  ].join("");
}
function estimateWordSize(text, fontSize, rotate) {
  const len = Math.max(1, text.length);
  const cjkCount = (text.match(/[\u3400-\u9fff]/g) || []).length;
  const cjkRatio = cjkCount / len;
  const widthFactor = 0.6 + cjkRatio * 0.45;
  const width = Math.max(14, len * fontSize * widthFactor);
  const height = Math.max(12, fontSize * 1.22);
  if (rotate === 90) return { w: height, h: width };
  return { w: width, h: height };
}
function intersects(a, b) {
  return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
}
function insideShape(x, y, w, h, boundsW, boundsH, layout) {
  const safetyX = 4;
  if (x < safetyX || y < 0 || x + w > boundsW - safetyX || y + h > boundsH) return false;
  if (layout !== "circle") return true;
  const MAX_ASPECT = 1.9;
  const cx = boundsW / 2;
  const cy = boundsH / 2;
  const ryBase = Math.max(1, boundsH / 2);
  const rxBase = Math.min(boundsW / 2, ryBase * MAX_ASPECT);
  const rx = Math.max(1, rxBase);
  const ry = Math.max(1, ryBase);
  const probes = [
    [x, y + h / 2],
    [x + w, y + h / 2],
    [x + w / 2, y],
    [x + w / 2, y + h]
  ];
  for (const [px, py] of probes) {
    const dx = (px - cx) / rx;
    const dy = (py - cy) / ry;
    if (dx * dx + dy * dy > 1) return false;
  }
  return true;
}
function placeWords(words, width, height, layout, orientation, padding, maxWords, legendSlots, categoryDataTokens) {
  const valid = words.filter((w) => w.text.trim() && Number.isFinite(w.value) && w.value > 0).sort((a, b) => b.value - a.value).slice(0, Math.max(1, maxWords));
  if (!valid.length || width < 20 || height < 20) return [];
  const minVal = Math.min(...valid.map((w) => w.value));
  const maxVal = Math.max(...valid.map((w) => w.value));
  const vSpan = Math.max(1, maxVal - minVal);
  const topWord = valid[0];
  const topLen = Math.max(1, String(topWord.text).length);
  const cjkCount = (String(topWord.text).match(/[\u3400-\u9fff]/g) || []).length;
  const cjkRatio = cjkCount / topLen;
  const widthFactor = 0.6 + cjkRatio * 0.45;
  const effectiveWidth = layout === "circle" ? Math.min(width, height * 1.9) : width;
  const widthBudget = effectiveWidth * (layout === "circle" ? 0.7 : 0.58);
  const heightBudget = height * (layout === "circle" ? 0.42 : 0.36);
  const fitByWidth = widthBudget / (topLen * widthFactor);
  const fitByHeight = heightBudget / 1.22;
  const dynamicMax = Math.max(20, Math.min(WORD_CLOUD_FONT_SIZES[1], Math.floor(Math.min(fitByWidth, fitByHeight))));
  const minFont = WORD_CLOUD_FONT_SIZES[0];
  const maxFont = dynamicMax;
  const minPlacedTarget = Math.min(valid.length, Math.max(Math.floor(valid.length * 0.9), valid.length - 2));
  const globalScales = [1, 0.94, 0.88, 0.82, 0.76, 0.7];
  const centerY = height * 0.5;
  let bestPlaced = [];
  const diag = Math.sqrt(width * width + height * height) / 2;
  const targetC = Math.max(3, Math.min(6, diag / 30));
  const maxSteps = 2400;
  const pp = padding + 2;
  function tryRandomPlace(placed, word, fontSize, rotate, color, attempts, seed) {
    const { w, h } = estimateWordSize(word.text, fontSize, rotate);
    for (let i = 0; i < attempts; i += 1) {
      const r1 = (seed * 9301 + i * 49297) % 233280 / 233280;
      const r2 = (seed * 7919 + i * 80021) % 233280 / 233280;
      const x = r1 * Math.max(1, width - w);
      const y = r2 * Math.max(1, height - h);
      if (!insideShape(x, y, w, h, width, height, layout)) continue;
      const box = { x: x - pp, y: y - pp, w: w + pp * 2, h: h + pp * 2 };
      if (placed.some((p) => intersects(box, { x: p.x - pp, y: p.y - pp, w: p.w + pp * 2, h: p.h + pp * 2 }))) continue;
      return { ...word, x, y, w, h, rotate, fontSize, color };
    }
    return null;
  }
  for (const globalScale of globalScales) {
    const placed = [];
    valid.forEach((word, wordIdx) => {
      const t = (word.value - minVal) / vSpan;
      const scaleForWord = t >= 0.98 ? 1 : globalScale;
      let fontSize = (minFont + t * (maxFont - minFont)) * scaleForWord;
      const rotate = orientation === "horizontal" ? 0 : simpleHash(word.text) % 3 === 0 ? 90 : 0;
      const color = wordFill(word, legendSlots, categoryDataTokens);
      let done = false;
      const isSmall = t < 0.25 || fontSize <= minFont * 1.6;
      if (isSmall) {
        const hit = tryRandomPlace(placed, word, fontSize, rotate, color, 80, simpleHash(word.text) + wordIdx);
        if (hit) {
          placed.push(hit);
          return;
        }
      }
      for (let shrink = 0; shrink < 10 && !done; shrink += 1) {
        const { w, h } = estimateWordSize(word.text, fontSize, rotate);
        const phase = simpleHash(word.text + String(wordIdx)) % 628 / 100;
        for (let step = 0; step < maxSteps; step += 1) {
          const theta = phase + step * 0.5;
          const r = targetC * Math.sqrt(step);
          const cx = width / 2 + Math.cos(theta) * r;
          const cy = centerY + Math.sin(theta) * r;
          const x = cx - w / 2;
          const y = cy - h / 2;
          const box = { x: x - pp, y: y - pp, w: w + pp * 2, h: h + pp * 2 };
          if (!insideShape(x, y, w, h, width, height, layout)) continue;
          if (placed.some((p) => intersects(box, { x: p.x - pp, y: p.y - pp, w: p.w + pp * 2, h: p.h + pp * 2 }))) continue;
          placed.push({ ...word, x, y, w, h, rotate, fontSize, color });
          done = true;
          break;
        }
        if (!done) {
          const hit = tryRandomPlace(placed, word, fontSize, rotate, color, 60, simpleHash(word.text) + wordIdx + shrink);
          if (hit) {
            placed.push(hit);
            done = true;
            break;
          }
        }
        fontSize *= 0.88;
        if (fontSize < minFont * 0.7) break;
      }
    });
    if (placed.length > bestPlaced.length) bestPlaced = placed;
    if (placed.length >= minPlacedTarget) return placed;
  }
  return bestPlaced;
}
function SmartWordCloud({
  data = [],
  datasets,
  activeDatasetId: activeDatasetIdProp,
  defaultDatasetId,
  onDatasetChange,
  chromeTitle,
  categoryCount: categoryCountProp,
  categoryLabels: categoryLabelsProp,
  categoryColorTokens: categoryColorTokensProp,
  showCategoryLegend: showCategoryLegendProp,
  showToneLegend: showToneLegendProp,
  orientation = "orthogonal",
  layout = "square",
  height = DEFAULT_HEIGHT_PX,
  wordPadding,
  showLegend = true,
  legendPosition: legendPositionProp = "bottom",
  maxWords = 120,
  className,
  style
}) {
  const plotRef = useRef(null);
  const rootRef = useRef(null);
  const [plotSize, setPlotSize] = useState({ width: 0, height: 0 });
  const [hovered, setHovered] = useState(null);
  useEffect(() => {
    if (!plotRef.current || typeof ResizeObserver === "undefined") return void 0;
    const ro = new ResizeObserver((entries) => {
      const rect = entries[0]?.contentRect;
      if (!rect) return;
      setPlotSize({ width: Math.max(0, rect.width), height: Math.max(0, rect.height) });
    });
    ro.observe(plotRef.current);
    return () => ro.disconnect();
  }, []);
  const datasetList = useMemo(
    () => Array.isArray(datasets) && datasets.length > 0 ? datasets : null,
    [datasets]
  );
  const useToolbar = Boolean(datasetList && datasetList.length >= 2);
  const firstId = datasetList?.[0]?.id ?? "default";
  const [internalId, setInternalId] = useState(
    defaultDatasetId && datasetList?.some((d) => d.id === defaultDatasetId) ? defaultDatasetId : firstId
  );
  const isControlled = activeDatasetIdProp !== void 0;
  useEffect(() => {
    if (!useToolbar || !datasetList || isControlled) return;
    if (defaultDatasetId && datasetList.some((d) => d.id === defaultDatasetId)) {
      setInternalId(defaultDatasetId);
    } else {
      setInternalId(firstId);
    }
  }, [useToolbar, datasetList, defaultDatasetId, firstId, isControlled]);
  const activeDatasetId = isControlled && datasetList?.some((d) => d.id === activeDatasetIdProp) ? activeDatasetIdProp : internalId;
  const setActiveDatasetId = useCallback(
    (id) => {
      if (!isControlled) setInternalId(id);
      onDatasetChange?.(id);
    },
    [isControlled, onDatasetChange]
  );
  const activeRows = useMemo(() => {
    if (datasetList && datasetList.length > 0) {
      const hit = datasetList.find((d) => d.id === activeDatasetId) ?? datasetList[0];
      return normalizeWords(hit?.data ?? []);
    }
    return normalizeWords(data);
  }, [datasetList, data, activeDatasetId]);
  const useCategoryMode = useMemo(
    () => activeRows.some((w) => typeof w.category === "number" && w.category >= 0),
    [activeRows]
  );
  const inferredSlots = useMemo(() => {
    if (!useCategoryMode) return 0;
    if (categoryCountProp != null) return clampCategoryCount(categoryCountProp);
    const m = Math.max(
      -1,
      ...activeRows.map((w) => typeof w.category === "number" ? w.category : -1)
    );
    return Math.min(5, Math.max(1, m + 1));
  }, [activeRows, categoryCountProp, useCategoryMode]);
  const legendSlots = inferredSlots;
  const categoryDataTokens = useMemo(() => {
    if (!categoryColorTokensProp?.length || legendSlots <= 0) return void 0;
    if (categoryColorTokensProp.length !== legendSlots) return void 0;
    return categoryColorTokensProp;
  }, [categoryColorTokensProp, legendSlots]);
  const categoryLabels = useMemo(() => {
    const base = categoryLabelsProp?.length ? categoryLabelsProp : [...DEFAULT_CATEGORY_LABELS];
    if (legendSlots <= 0) return base;
    return Array.from({ length: legendSlots }, (_, i) => base[i] ?? DEFAULT_CATEGORY_LABELS[i] ?? `\u7C7B\u578B ${i + 1}`);
  }, [categoryLabelsProp, legendSlots]);
  const legendVisible = showLegend !== false;
  const legendPos = ["top", "bottom"].includes(legendPositionProp) ? legendPositionProp : "bottom";
  const legendVertical = false;
  const categoryLegendEligible = showCategoryLegendProp !== false && useCategoryMode && legendSlots >= 1 && legendSlots <= 5;
  const toneLegendEligible = showToneLegendProp !== false && !useCategoryMode && activeRows.some((w) => w.tone === "positive" || w.tone === "neutral" || w.tone === "negative");
  const showCategoryLegend = categoryLegendEligible && legendVisible;
  const showToneLegend = toneLegendEligible && legendVisible;
  const hasLegendBlock = showCategoryLegend || showToneLegend;
  const useCardChrome = useToolbar || Boolean(chromeTitle) || hasLegendBlock;
  const placementPadding = wordPadding != null ? Math.max(0, wordPadding) : layout === "circle" ? 3 : 2;
  const placedWords = useMemo(
    () => placeWords(
      activeRows,
      Math.max(0, plotSize.width),
      Math.max(0, plotSize.height),
      layout,
      orientation,
      placementPadding,
      maxWords,
      legendSlots,
      categoryDataTokens
    ),
    [activeRows, plotSize.width, plotSize.height, layout, orientation, placementPadding, maxWords, legendSlots, categoryDataTokens]
  );
  const legendBlock = useMemo(() => {
    const legCls = `smart-wordcloud__legend${legendVertical ? " smart-wordcloud__legend--vertical" : ""}`;
    if (showCategoryLegend && legendSlots > 0) {
      return /* @__PURE__ */ jsx("div", { className: legCls, "aria-label": "\u8BCD\u4E91\u7C7B\u578B\u56FE\u4F8B", children: Array.from({ length: legendSlots }, (_, i) => {
        const tok = categoryDataTokens?.[i];
        const v = tok ? tokenNameToCssVar(tok) : "";
        return /* @__PURE__ */ jsxs("span", { className: "smart-wordcloud__legend-item", children: [
          v ? /* @__PURE__ */ jsx(
            "span",
            {
              className: "smart-wordcloud__dot",
              style: { background: `var(${v})` }
            }
          ) : /* @__PURE__ */ jsx(
            "span",
            {
              className: `smart-wordcloud__dot smart-wordcloud__dot--cat${i % WORD_CLOUD_CATEGORY_FILLS.length}`
            }
          ),
          categoryLabels[i] ?? `\u7C7B\u578B ${i + 1}`
        ] }, i);
      }) });
    }
    if (showToneLegend) {
      return /* @__PURE__ */ jsxs("div", { className: legCls, "aria-hidden": !showToneLegend, children: [
        /* @__PURE__ */ jsxs("span", { className: "smart-wordcloud__legend-item", children: [
          /* @__PURE__ */ jsx("span", { className: "smart-wordcloud__dot smart-wordcloud__dot--positive" }),
          "\u6B63\u5411"
        ] }),
        /* @__PURE__ */ jsxs("span", { className: "smart-wordcloud__legend-item", children: [
          /* @__PURE__ */ jsx("span", { className: "smart-wordcloud__dot smart-wordcloud__dot--neutral" }),
          "\u4E2D\u7ACB"
        ] }),
        /* @__PURE__ */ jsxs("span", { className: "smart-wordcloud__legend-item", children: [
          /* @__PURE__ */ jsx("span", { className: "smart-wordcloud__dot smart-wordcloud__dot--negative" }),
          "\u8D1F\u5411"
        ] })
      ] });
    }
    return null;
  }, [
    showCategoryLegend,
    showToneLegend,
    legendSlots,
    legendVertical,
    categoryLabels,
    categoryDataTokens
  ]);
  const toolbarNode = useToolbar && datasetList ? /* @__PURE__ */ jsx("div", { className: "smart-wordcloud__toolbar", children: /* @__PURE__ */ jsxs(
    "div",
    {
      className: "smart-wordcloud__chrome-row smart-wordcloud__chrome-row--toolbar",
      style: { justifyContent: chromeTitle ? "space-between" : "flex-end" },
      children: [
        chromeTitle ? /* @__PURE__ */ jsx("div", { className: "smart-wordcloud__chrome-left", children: /* @__PURE__ */ jsx("div", { className: "smart-wordcloud__title", children: chromeTitle }) }) : null,
        /* @__PURE__ */ jsx("div", { className: "smart-wordcloud__segments", role: "tablist", "aria-label": "\u8BCD\u4E91\u6570\u636E\u5207\u6362", children: datasetList.map((ds) => {
          const on = ds.id === activeDatasetId;
          return /* @__PURE__ */ jsxs(
            "button",
            {
              type: "button",
              role: "tab",
              "aria-selected": on,
              className: `smart-wordcloud__seg${on ? " smart-wordcloud__seg--active" : ""}`,
              onClick: () => setActiveDatasetId(ds.id),
              children: [
                /* @__PURE__ */ jsx("span", { className: "smart-wordcloud__seg-label", children: ds.label }),
                ds.badge != null && ds.badge !== "" ? /* @__PURE__ */ jsx("span", { className: "smart-wordcloud__seg-badge", children: ds.badge }) : null
              ]
            },
            ds.id
          );
        }) })
      ]
    }
  ) }) : chromeTitle ? /* @__PURE__ */ jsx("div", { className: "smart-wordcloud__toolbar", children: /* @__PURE__ */ jsx("div", { className: "smart-wordcloud__title", children: chromeTitle }) }) : null;
  const rootClass = [
    "smart-wordcloud",
    useCardChrome ? "smart-wordcloud--card" : "",
    layout === "circle" ? "smart-wordcloud--circle" : "smart-wordcloud--square",
    className || ""
  ].filter(Boolean).join(" ");
  const containerStyle = {
    width: "100%",
    height,
    minHeight: 0,
    boxSizing: "border-box",
    display: "flex",
    flexDirection: "column",
    ...style
  };
  return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: rootClass, style: containerStyle, children: [
    toolbarNode,
    /* @__PURE__ */ jsxs("div", { className: `smart-wordcloud__body smart-wordcloud__body--legend-${legendPos}`, children: [
      legendPos === "top" && legendBlock,
      /* @__PURE__ */ jsx("div", { className: "smart-wordcloud__canvas", children: /* @__PURE__ */ jsx("div", { ref: plotRef, className: "smart-wordcloud__plot smart-wordcloud__plot--html", children: placedWords.map((w, idx) => /* @__PURE__ */ jsx(
        "button",
        {
          type: "button",
          className: "smart-wordcloud__word",
          style: {
            left: w.x,
            top: w.y,
            width: w.w,
            height: w.h,
            color: w.color,
            fontSize: w.fontSize,
            ["--wc-base-transform"]: `rotate(${w.rotate}deg)`,
            transform: "var(--wc-base-transform)"
          },
          title: wordTooltipHtml(w, legendSlots, categoryLabels, categoryDataTokens).replace(/<br\/>/g, "\n").replace(/<[^>]*>/g, ""),
          onMouseEnter: (event) => {
            const rect = event.currentTarget.getBoundingClientRect();
            setHovered({
              word: w,
              x: rect.left + rect.width / 2,
              y: rect.top
            });
          },
          onMouseLeave: () => setHovered(null),
          children: /* @__PURE__ */ jsx("span", { className: "smart-wordcloud__word-inner", children: w.text })
        },
        `${w.text}-${idx}`
      )) }) }),
      legendPos === "bottom" && legendBlock
    ] }),
    hovered ? /* @__PURE__ */ jsx(FloatingTooltip, { mouse: { clientX: hovered.x, clientY: hovered.y }, children: /* @__PURE__ */ jsx(
      "div",
      {
        className: "smart-wordcloud__tooltip-shell",
        dangerouslySetInnerHTML: {
          __html: wordTooltipHtml(hovered.word, legendSlots, categoryLabels, categoryDataTokens)
        }
      }
    ) }) : null
  ] });
}
export {
  THEME_COLORS,
  WORD_CLOUD_CATEGORY_FILLS,
  WORD_CLOUD_FONT_SIZES,
  SmartWordCloud as default
};
