// Visual primitives: badges, buttons, sparklines, kbd, etc.
const { useState, useEffect, useRef, useMemo } = React;

function classNames(...xs) { return xs.filter(Boolean).join(" "); }

// ── Status dot ──────────────────────────────────────────────────────────
function StatusDot({ status = "ok", blink = false, size = 8 }) {
  const map = {
    ok: "var(--ok)",
    warn: "var(--warn)",
    err: "var(--err)",
    info: "var(--info)",
    idle: "var(--fg-3)",
  };
  return (
    <span
      className={blink ? "blink" : ""}
      style={{
        width: size,
        height: size,
        borderRadius: 999,
        background: map[status] || map.idle,
        display: "inline-block",
        boxShadow: blink ? `0 0 8px ${map[status]}` : "none",
        flex: "none",
      }}
    />
  );
}

// ── Badge ───────────────────────────────────────────────────────────────
function Badge({ children, tone = "neutral", subtle = false, mono = false, style }) {
  const tones = {
    neutral: { fg: "var(--fg-1)", bg: "var(--bg-3)", bd: "var(--line)" },
    ok:      { fg: "var(--ok)",   bg: "var(--ok-bg)",   bd: "color-mix(in oklch, var(--ok) 35%, transparent)" },
    warn:    { fg: "var(--warn)", bg: "var(--warn-bg)", bd: "color-mix(in oklch, var(--warn) 35%, transparent)" },
    err:     { fg: "var(--err)",  bg: "var(--err-bg)",  bd: "color-mix(in oklch, var(--err) 35%, transparent)" },
    info:    { fg: "var(--info)", bg: "var(--info-bg)", bd: "color-mix(in oklch, var(--info) 35%, transparent)" },
    btc:     { fg: "var(--btc)",  bg: "color-mix(in oklch, var(--btc) 14%, transparent)", bd: "color-mix(in oklch, var(--btc) 30%, transparent)" },
    zec:     { fg: "var(--zec)",  bg: "color-mix(in oklch, var(--zec) 14%, transparent)", bd: "color-mix(in oklch, var(--zec) 30%, transparent)" },
    alice:   { fg: "var(--alice)",bg: "color-mix(in oklch, var(--alice) 14%, transparent)", bd: "color-mix(in oklch, var(--alice) 30%, transparent)" },
    bob:     { fg: "var(--bob)",  bg: "color-mix(in oklch, var(--bob) 14%, transparent)", bd: "color-mix(in oklch, var(--bob) 30%, transparent)" },
  };
  const t = tones[tone] || tones.neutral;
  return (
    <span
      className={mono ? "mono" : ""}
      style={{
        display: "inline-flex",
        alignItems: "center",
        gap: 5,
        padding: subtle ? "1px 6px" : "2px 7px",
        fontSize: 11,
        fontWeight: 500,
        borderRadius: 4,
        color: t.fg,
        background: subtle ? "transparent" : t.bg,
        border: `1px solid ${subtle ? "transparent" : t.bd}`,
        whiteSpace: "nowrap",
        lineHeight: 1.4,
        ...style,
      }}
    >
      {children}
    </span>
  );
}

// ── Button ──────────────────────────────────────────────────────────────
function Button({ children, onClick, variant = "ghost", size = "sm", danger, icon, style, title, disabled }) {
  const sizes = {
    sm: { pad: "4px 9px", fs: 12, h: 26 },
    md: { pad: "6px 12px", fs: 13, h: 30 },
  };
  const sz = sizes[size];
  const variants = {
    ghost: {
      bg: "transparent",
      bgHover: "var(--bg-hover)",
      bd: "var(--line)",
      fg: "var(--fg-1)",
    },
    solid: {
      bg: "var(--bg-3)",
      bgHover: "var(--bg-hover)",
      bd: "var(--line)",
      fg: "var(--fg-0)",
    },
    primary: {
      bg: "color-mix(in oklch, var(--alice) 22%, var(--bg-2))",
      bgHover: "color-mix(in oklch, var(--alice) 32%, var(--bg-2))",
      bd: "color-mix(in oklch, var(--alice) 50%, transparent)",
      fg: "var(--fg-0)",
    },
  };
  const v = variants[variant];
  const fg = danger ? "var(--err)" : v.fg;
  const [hover, setHover] = useState(false);
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      disabled={disabled}
      title={title}
      style={{
        background: hover && !disabled ? v.bgHover : v.bg,
        border: `1px solid ${v.bd}`,
        color: fg,
        height: sz.h,
        padding: sz.pad,
        fontSize: sz.fs,
        fontWeight: 500,
        borderRadius: 5,
        display: "inline-flex",
        alignItems: "center",
        gap: 6,
        cursor: disabled ? "not-allowed" : "pointer",
        opacity: disabled ? 0.5 : 1,
        transition: "background 120ms",
        ...style,
      }}
    >
      {icon}{children}
    </button>
  );
}

// ── Kbd ─────────────────────────────────────────────────────────────────
function Kbd({ children }) {
  return (
    <kbd
      style={{
        fontFamily: "var(--mono)",
        fontSize: 10,
        padding: "1px 5px",
        background: "var(--bg-3)",
        border: "1px solid var(--line)",
        borderBottomWidth: 2,
        borderRadius: 3,
        color: "var(--fg-1)",
      }}
    >{children}</kbd>
  );
}

// ── Hash chip (copyable display of txid/address) ────────────────────────
function HashChip({ value, label, headLen = 6, tailLen = 4, prefix }) {
  const [copied, setCopied] = useState(false);
  if (!value || value === ZW.NA) return <span style={{ color: "var(--fg-3)" }}>{ZW.NA}</span>;
  const onCopy = (e) => {
    e.stopPropagation();
    try { navigator.clipboard?.writeText(value); } catch {}
    setCopied(true);
    setTimeout(() => setCopied(false), 900);
  };
  return (
    <span
      onClick={onCopy}
      title={`${label || "copy"}: ${value}`}
      className="mono"
      style={{
        display: "inline-flex",
        alignItems: "center",
        gap: 4,
        fontSize: 11.5,
        color: "var(--fg-1)",
        cursor: "pointer",
        userSelect: "none",
      }}
    >
      {prefix && <span style={{ color: "var(--fg-3)" }}>{prefix}</span>}
      <span>{ZW.shortHash(value, headLen, tailLen)}</span>
      <span style={{
        opacity: copied ? 1 : 0.5,
        color: copied ? "var(--ok)" : "var(--fg-3)",
        fontSize: 10,
        transition: "opacity 120ms",
      }}>{copied ? "✓" : "⧉"}</span>
    </span>
  );
}

// ── Sparkline ──────────────────────────────────────────────────────────
function Sparkline({ data, width = 96, height = 22, color = "var(--alice)", fill = true }) {
  if (!data || data.length === 0) return null;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const stepX = width / (data.length - 1 || 1);
  const points = data.map((v, i) => [i * stepX, height - ((v - min) / range) * (height - 2) - 1]);
  const pathD = points.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
  const fillD = `${pathD} L ${width} ${height} L 0 ${height} Z`;
  return (
    <svg width={width} height={height} style={{ display: "block" }}>
      {fill && (
        <path d={fillD} fill={color} opacity={0.12} />
      )}
      <path d={pathD} fill="none" stroke={color} strokeWidth={1.4} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// ── Card / Panel ───────────────────────────────────────────────────────
function Panel({ title, right, children, style, padded = true, scrollable = false }) {
  return (
    <section
      style={{
        background: "var(--bg-1)",
        border: "1px solid var(--line-soft)",
        borderRadius: "var(--radius-lg)",
        display: "flex",
        flexDirection: "column",
        minHeight: 0,
        ...style,
      }}
    >
      {title && (
        <header style={{
          padding: "10px 14px",
          borderBottom: "1px solid var(--line-soft)",
          display: "flex",
          alignItems: "center",
          justifyContent: "space-between",
          flex: "none",
        }}>
          <h3 style={{
            margin: 0, fontSize: 12, fontWeight: 600,
            letterSpacing: 0.4, textTransform: "uppercase", color: "var(--fg-2)",
          }}>{title}</h3>
          {right}
        </header>
      )}
      <div style={{
        padding: padded ? "12px 14px" : 0,
        flex: 1,
        minHeight: 0,
        overflow: scrollable ? "auto" : "visible",
      }}>
        {children}
      </div>
    </section>
  );
}

// ── KPI block ──────────────────────────────────────────────────────────
function KPI({ label, value, unit, delta, deltaTone, sparkline, sparkColor, hint }) {
  const dTone = {
    up: "var(--ok)", down: "var(--err)", flat: "var(--fg-3)",
  }[deltaTone] || "var(--fg-3)";
  return (
    <div style={{
      background: "var(--bg-1)",
      border: "1px solid var(--line-soft)",
      borderRadius: "var(--radius-lg)",
      padding: "12px 14px",
      display: "flex",
      flexDirection: "column",
      gap: 6,
      minWidth: 0,
    }}>
      <div style={{ fontSize: 11, color: "var(--fg-2)", letterSpacing: 0.4, textTransform: "uppercase", fontWeight: 600 }}>{label}</div>
      <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 12 }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 4, minWidth: 0 }}>
          <span className="mono" style={{ fontSize: 22, fontWeight: 600, color: "var(--fg-0)", letterSpacing: -0.2 }}>{value === null || value === undefined || value === "" ? ZW.NA : value}</span>
          {unit && <span style={{ fontSize: 12, color: "var(--fg-2)" }}>{unit}</span>}
        </div>
        {sparkline && <Sparkline data={sparkline} color={sparkColor || "var(--alice)"} />}
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 11, color: "var(--fg-3)" }}>
        {delta && <span style={{ color: dTone, fontWeight: 600 }}>{delta}</span>}
        {hint && <span>{hint}</span>}
      </div>
    </div>
  );
}

// ── Row separator ──────────────────────────────────────────────────────
function HSep({ style }) {
  return <div style={{ height: 1, background: "var(--line-soft)", ...style }} />;
}

// ── Empty state ────────────────────────────────────────────────────────
function Empty({ children }) {
  return (
    <div style={{
      padding: "40px 20px", textAlign: "center", color: "var(--fg-3)", fontSize: 12,
    }}>{children}</div>
  );
}

// State chip — colors keyed to FSM branch
function StateChip({ state, size = "sm" }) {
  const T = ZW.TERMINAL;
  const tone =
    state === "alice_redeemed" ? "ok" :
    state === "alice_refunded" ? "info" :
    state === "alice_claimed_after_refund" ? "info" :
    state === "bob_refunded" ? "info" :
    state === "bob_claimed_from_refund" ? "info" :
    ZW.REFUND_BRANCH.includes(state) ? "warn" :
    ZW.CLAIM_BRANCH.includes(state) ? "warn" :
    "alice";
  return (
    <Badge tone={tone} subtle={false}>
      <StatusDot status={
        tone === "ok" ? "ok" :
        tone === "warn" ? "warn" :
        tone === "info" ? "info" :
        "info"
      } size={6} />
      {ZW.STATE_LABELS[state] || state}
    </Badge>
  );
}

// Direction arrow chip
function DirectionChip({ direction }) {
  if (!direction || direction === ZW.NA) return <span className="mono" style={{ color: "var(--fg-3)" }}>{ZW.NA}</span>;
  const isB2Z = direction === "btc-to-zec";
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 4, fontSize: 11.5, fontWeight: 500,
    }}>
      <span style={{ color: isB2Z ? "var(--btc)" : "var(--zec)" }}>{isB2Z ? "BTC" : "ZEC"}</span>
      <span style={{ color: "var(--fg-3)" }}>→</span>
      <span style={{ color: isB2Z ? "var(--zec)" : "var(--btc)" }}>{isB2Z ? "ZEC" : "BTC"}</span>
    </span>
  );
}

Object.assign(window, {
  classNames, StatusDot, Badge, Button, Kbd, HashChip, Sparkline, Panel, KPI, HSep, Empty,
  StateChip, DirectionChip,
});
