/* global React */
// =====================================================================
// Reusable widgets: animated number, sparkline, charts, mini bits
// =====================================================================

const { useState, useEffect, useRef, useMemo } = React;

// ---------- formatting helpers ----------
const fmt = {
  money: (n, opts = {}) => {
    const { compact = false, sign = false } = opts;
    if (n == null || isNaN(n)) return "—";
    const neg = n < 0;
    const abs = Math.abs(n);
    let str;
    if (compact && abs >= 1000000) str = (abs/1000000).toFixed(2) + "M";
    else if (compact && abs >= 1000) str = (abs/1000).toFixed(abs >= 10000 ? 1 : 2) + "k";
    else str = Math.round(abs).toLocaleString("en-US");
    const s = "$" + str;
    if (neg) return "−" + s;
    if (sign && n > 0) return "+" + s;
    return s;
  },
  num: (n, opts = {}) => {
    if (n == null || isNaN(n)) return "—";
    const { compact = false } = opts;
    if (compact && Math.abs(n) >= 1000) return (n/1000).toFixed(n>=10000?0:1) + "k";
    return Math.round(n).toLocaleString("en-US");
  },
  pct: (n, opts = {}) => {
    if (n == null || isNaN(n)) return "—";
    const { sign = false, digits = 1 } = opts;
    const v = Number(n).toFixed(digits);
    return (sign && n > 0 ? "+" : "") + v + "%";
  },
};

// ---------- AnimatedNumber: ticker effect ----------
function AnimatedNumber({ value, format = "num", duration = 900, className = "", ...rest }) {
  const [display, setDisplay] = useState(value);
  const fromRef = useRef(value);
  const startRef = useRef(performance.now());

  useEffect(() => {
    fromRef.current = display;
    startRef.current = performance.now();
    let raf;
    const tick = (t) => {
      const k = Math.min(1, (t - startRef.current) / duration);
      const eased = 1 - Math.pow(1 - k, 3);
      setDisplay(fromRef.current + (value - fromRef.current) * eased);
      if (k < 1) raf = requestAnimationFrame(tick);
      else setDisplay(value);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
    // eslint-disable-next-line
  }, [value]);

  let txt;
  if (format === "money") txt = fmt.money(display, rest);
  else if (format === "moneyk") txt = fmt.money(display, { compact: true });
  else if (format === "pct") txt = fmt.pct(display, rest);
  else if (format === "num") txt = fmt.num(display);
  else if (format === "numk") txt = fmt.num(display, { compact: true });
  else txt = fmt.num(display);

  return <span className={"op-num " + className}>{txt}</span>;
}

// ---------- Sparkline ----------
function Sparkline({ data, color = "currentColor", strokeWidth = 1.5, fill = false, height = 28, className = "" }) {
  if (!data || data.length < 2) return null;
  const w = 120;
  const h = height;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const step = w / (data.length - 1);
  const pts = data.map((v, i) => `${(i*step).toFixed(2)},${(h - ((v - min)/range)*h).toFixed(2)}`);
  const path = "M" + pts.join(" L");
  const area = path + ` L${w},${h} L0,${h} Z`;
  return (
    <svg className={"spark " + className} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none">
      {fill && <path d={area} fill={color} opacity="0.18" />}
      <path d={path} fill="none" stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}

// ---------- AreaSparkline (with gradient) ----------
function AreaSpark({ data, gradId = "g1", color = "var(--wm-orange)", height = 22 }) {
  if (!data || data.length < 2) return null;
  const w = 600;
  const h = height;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const step = w / (data.length - 1);
  const pts = data.map((v, i) => `${(i*step).toFixed(2)},${(h - ((v - min)/range)*h).toFixed(2)}`);
  const path = "M" + pts.join(" L");
  const area = path + ` L${w},${h} L0,${h} Z`;
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" className="profit-spark">
      <defs>
        <linearGradient id={gradId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.55" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill={`url(#${gradId})`} />
      <path d={path} fill="none" stroke={color} strokeWidth="1.4" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}

// ---------- Trend pill ----------
function Delta({ value, suffix = "%", inverse = false }) {
  if (value == null) return null;
  const pos = inverse ? value < 0 : value > 0;
  const neg = inverse ? value > 0 : value < 0;
  const cls = pos ? "up" : neg ? "down" : "flat";
  const arrow = pos ? "↑" : neg ? "↓" : "→";
  return (
    <span className={"profit-delta " + cls}>
      {arrow} {Math.abs(value).toFixed(1)}{suffix}
    </span>
  );
}

function Trend({ value, suffix = "%", inverse = false, size = 11 }) {
  if (value == null) return null;
  const pos = inverse ? value < 0 : value > 0;
  const neg = inverse ? value > 0 : value < 0;
  const cls = pos ? "up" : neg ? "down" : "flat";
  return (
    <span className={"trend " + cls} style={{ fontSize: size }}>
      {pos ? "▲" : neg ? "▼" : "■"} {Math.abs(value).toFixed(1)}{suffix}
    </span>
  );
}

// ---------- Status pill ----------
function StatusPill({ status, lang = "es", size = "sm" }) {
  const t = window.i18n[lang].common;
  const map = {
    scale:    { cls: "pill-scale",    label: t.scale,    dot: true },
    optimize: { cls: "pill-optimize", label: t.optimize, dot: true },
    kill:     { cls: "pill-kill",     label: t.kill,     dot: true },
    watch:    { cls: "pill-watch",    label: t.watch,    dot: true },
  };
  const m = map[status] || map.watch;
  return (
    <span className={"pill " + m.cls + (size === "lg" ? " pill-lg" : "")}>
      <span className="dot" />{m.label.toUpperCase()}
    </span>
  );
}

// ---------- Lucide icon helper ----------
// Renders an inline <i data-lucide> and triggers lucide.createIcons() once mounted.
function Icon({ name, size = 16, className = "", style = {}, strokeWidth = 1.75 }) {
  const ref = useRef(null);
  useEffect(() => {
    if (window.lucide && ref.current) {
      try { window.lucide.createIcons({ icons: window.lucide.icons, attrs: { "stroke-width": strokeWidth }, nameAttr: "data-lucide" }); }
      catch(e) { /* ignore */ }
    }
  }, [name]);
  return (
    <i
      ref={ref}
      data-lucide={name}
      style={{ width: size, height: size, display: "inline-flex", alignItems: "center", justifyContent: "center", ...style }}
      className={className}
    />
  );
}

// ---------- CellBar (in-table progress) ----------
function CellBar({ value, max = 100, color = "blue", showVal = true, suffix = "%" }) {
  const pct = Math.max(0, Math.min(100, (value / max) * 100));
  return (
    <div className="cell-bar">
      <div className="cell-bar-track">
        <div className={"cell-bar-fill " + color} style={{ width: pct + "%" }} />
      </div>
      {showVal && <span style={{ width: 42, textAlign: "right" }}>{Number(value).toFixed(1)}{suffix}</span>}
    </div>
  );
}

// ---------- Donut chart ----------
function Donut({ value, max = 100, size = 90, stroke = 10, color = "var(--wm-blue)", trackColor = "var(--op-line)", label = "", sub = "" }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const pct = Math.max(0, Math.min(1, value / max));
  const dash = c * pct;
  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg width={size} height={size}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={trackColor} strokeWidth={stroke} />
        <circle
          cx={size/2} cy={size/2} r={r}
          fill="none" stroke={color} strokeWidth={stroke}
          strokeDasharray={`${dash} ${c}`}
          strokeLinecap="round"
          transform={`rotate(-90 ${size/2} ${size/2})`}
          style={{ transition: "stroke-dasharray 700ms cubic-bezier(.2,.8,.2,1)" }}
        />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", textAlign: "center" }}>
        <div>
          <div style={{ fontWeight: 900, fontSize: size * 0.22, color: "var(--wm-blue)", lineHeight: 1, fontVariantNumeric: "tabular-nums" }}>{label}</div>
          {sub && <div style={{ fontSize: 10, color: "var(--wm-fg-muted)", marginTop: 2, fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase" }}>{sub}</div>}
        </div>
      </div>
    </div>
  );
}

// ---------- BarChart ----------
function BarChart({ data, height = 160, color = "var(--wm-blue)", labels = true, valueKey = "value", labelKey = "label", format = (v)=>v }) {
  if (!data || !data.length) return null;
  const max = Math.max(...data.map(d => d[valueKey]));
  return (
    <div style={{ display: "flex", alignItems: "flex-end", gap: 8, height, paddingBottom: labels ? 22 : 0, position: "relative" }}>
      {data.map((d, i) => {
        const h = (d[valueKey] / max) * (height - (labels ? 30 : 6));
        return (
          <div key={i} style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", gap: 4, height: "100%" }}>
            <div style={{ flex: 1, width: "100%", display: "flex", alignItems: "flex-end", justifyContent: "center" }}>
              <div style={{
                width: "70%",
                height: h,
                background: color,
                borderRadius: "6px 6px 2px 2px",
                position: "relative",
                transition: "height 600ms cubic-bezier(.2,.8,.2,1)",
              }}>
                <span style={{
                  position: "absolute", top: -16, left: "50%", transform: "translateX(-50%)",
                  fontSize: 10, fontWeight: 800, color: "var(--wm-blue)", whiteSpace: "nowrap", fontVariantNumeric: "tabular-nums",
                }}>{format(d[valueKey])}</span>
              </div>
            </div>
            {labels && (
              <span style={{ fontSize: 10, fontWeight: 700, color: "var(--wm-fg-muted)", letterSpacing: "0.04em", textTransform: "uppercase", whiteSpace: "nowrap" }}>{d[labelKey]}</span>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ---------- LiveDot ----------
function LiveDot() {
  return (
    <span style={{
      display: "inline-block", width: 6, height: 6, borderRadius: 999,
      background: "var(--op-green)",
      boxShadow: "0 0 0 0 rgba(27,138,90,0.6)",
      animation: "pulse 1.6s infinite",
    }} />
  );
}

// CSS keyframes for the live dot — injected once
(function injectKeyframes(){
  if (document.getElementById("__op-keyframes")) return;
  const s = document.createElement("style");
  s.id = "__op-keyframes";
  s.textContent = `
    @keyframes pulse {
      0% { box-shadow: 0 0 0 0 rgba(27,138,90,0.55); }
      70% { box-shadow: 0 0 0 6px rgba(27,138,90,0); }
      100% { box-shadow: 0 0 0 0 rgba(27,138,90,0); }
    }
    @keyframes flash-up { 0% { background: rgba(27,138,90,0.18); } 100% { background: transparent; } }
    @keyframes flash-down { 0% { background: rgba(181,48,31,0.18); } 100% { background: transparent; } }
    .flash-up { animation: flash-up 700ms ease-out; }
    .flash-down { animation: flash-down 700ms ease-out; }
  `;
  document.head.appendChild(s);
})();

// ---------- Heat color helper ----------
function heatColor(v, min, max) {
  if (v == null) return "var(--op-line)";
  if (v < 0) {
    const t = Math.min(1, Math.abs(v) / Math.abs(min || 1));
    // red scale
    return `rgba(181, 48, 31, ${0.10 + t * 0.55})`;
  }
  const t = Math.min(1, v / (max || 1));
  // blue→orange scale by intensity, orange only at the top
  if (t > 0.78) return `rgba(248, 155, 36, ${0.45 + (t-0.78)*2})`;
  if (t > 0.45) return `rgba(29, 66, 155, ${0.18 + t * 0.50})`;
  return `rgba(29, 66, 155, ${0.06 + t * 0.30})`;
}

Object.assign(window, {
  fmt,
  AnimatedNumber, Sparkline, AreaSpark, Delta, Trend, StatusPill, Icon,
  CellBar, Donut, BarChart, LiveDot, heatColor,
});
