// Primitives — reusable building blocks for the Swivy console

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

// --- Status pill
const Status = ({ kind, children }) => {
  const labels = {
    active: "Active", pending: "Pending", revoked: "Revoked",
    verified: "Verified", failed: "Failed", action: "Requires action", neutral: children,
  };
  return (
    <span className={`status status-${kind}`}>
      <span className="dot" />
      {children || labels[kind]}
    </span>
  );
};

// --- Truncated id / address with copy
const MonoId = ({ children, full }) => {
  const [copied, setCopied] = useState(false);
  const onCopy = (e) => {
    e.stopPropagation();
    navigator.clipboard?.writeText(full || children);
    setCopied(true);
    setTimeout(() => setCopied(false), 1200);
  };
  return (
    <span className="truncate-id" onClick={onCopy} title={full || children}>
      <span>{children}</span>
      {copied ? <ICheck className="copy-icn" size={11} /> : <ICopy className="copy-icn" size={11} />}
    </span>
  );
};

// --- Chain badge
const Chain = ({ chain }) => {
  const c = CHAINS.find(x => x.key === chain) || { key: chain, label: chain, glyph: chain[0] };
  return (
    <span className="chain">
      <span className="chain-glyph">{c.glyph}</span>
      {c.label}
    </span>
  );
};

// --- Button
const Btn = (props) => {
  const { variant = "secondary", size, children, icon, iconRight, ...rest } = props;
  return (
    <button className={`btn btn-${variant} ${size ? `btn-${size}` : ""}`} {...rest}>
      {icon}
      {children && <span>{children}</span>}
      {iconRight}
    </button>
  );
};

// --- Card
const Card = ({ title, sub, actions, children, bodyClass = "" }) => (
  <div className="card">
    {(title || actions) && (
      <div className="card-hd">
        <div style={{ flex: 1 }}>
          {title && <div className="card-title">{title}</div>}
          {sub && <div className="card-sub">{sub}</div>}
        </div>
        {actions}
      </div>
    )}
    <div className={`card-body ${bodyClass}`}>{children}</div>
  </div>
);

// --- Field
const Field = ({ label, hint, error, required, children }) => (
  <div className="field">
    {label && (
      <label className="field-label">
        {label} {required && <span className="req">*</span>}
      </label>
    )}
    {children}
    {hint && !error && <div className="field-hint">{hint}</div>}
    {error && <div className="field-error"><IWarn size={11}/> {error}</div>}
  </div>
);

// --- Segmented
const Segmented = ({ value, onChange, options }) => (
  <div className="segmented" role="tablist">
    {options.map(o => (
      <button key={o.value} className={value === o.value ? "on" : ""} onClick={() => onChange(o.value)}>
        {o.label}
      </button>
    ))}
  </div>
);

// --- Toggle
const Toggle = ({ on, onChange }) => (
  <button className={`toggle ${on ? "on" : ""}`} onClick={() => onChange(!on)} aria-pressed={on} />
);

// --- Code block with file tabs
const CodeBlock = ({ files, defaultLang, style = "filename-tabs" }) => {
  const [active, setActive] = useState(0);
  const f = files[active];
  if (style === "minimal") {
    return (
      <div className="code">
        <div className="code-header">
          <span className="mono" style={{ color: "var(--ink-3)", fontSize: 11.5 }}>{f.name}</span>
          <span className="code-lang">{f.lang}</span>
        </div>
        <pre className="code-body" style={{ margin: 0 }}><Highlight code={f.code} /></pre>
      </div>
    );
  }
  if (style === "terminal") {
    return (
      <div className="code" style={{ background: "var(--bg-code)" }}>
        <div className="code-header" style={{ paddingLeft: 12 }}>
          <span style={{ display: "flex", gap: 5 }}>
            <span style={{ width: 9, height: 9, borderRadius: "50%", background: "var(--line-strong)" }} />
            <span style={{ width: 9, height: 9, borderRadius: "50%", background: "var(--line-strong)" }} />
            <span style={{ width: 9, height: 9, borderRadius: "50%", background: "var(--line-strong)" }} />
          </span>
          <span className="mono" style={{ marginLeft: 12, fontSize: 11.5, color: "var(--ink-3)" }}>{f.name}</span>
        </div>
        <pre className="code-body" style={{ margin: 0 }}><Highlight code={f.code} /></pre>
      </div>
    );
  }
  // filename-tabs (default)
  return (
    <div className="code">
      <div className="code-header" style={{ padding: "0 6px 0 6px" }}>
        {files.map((file, i) => (
          <span key={i} className={`code-tab ${i === active ? "on" : ""}`} onClick={() => setActive(i)}>
            {file.name}
          </span>
        ))}
        <span className="code-lang">{f.lang}</span>
        <button className="icon-btn" style={{ width: 26, height: 26 }} title="Copy"><ICopy size={13}/></button>
      </div>
      <pre className="code-body" style={{ margin: 0 }}><Highlight code={f.code} /></pre>
    </div>
  );
};

// --- Very small syntax highlighter (regex-based, enough for snippets)
const Highlight = ({ code }) => {
  // Tokenize line-by-line; avoid regex lookbehind for safety.
  const lines = code.split("\n");
  const KEYS = /\b(const|let|var|import|from|export|default|return|async|await|function|new|if|else|true|false|null|undefined|void|class|this|as|in)\b/g;
  const FNS = /\b([A-Za-z_$][\w$]*)(?=\()/g;
  const NUMS = /\b(\d+\.?\d*)\b/g;
  const STRS = /(["'`])((?:\\.|(?!\1).)*)\1/g;
  const COMMS = /(\/\/[^\n]*)/g;
  const renderLine = (line, ln) => {
    // comments first — return raw tokens to avoid replacing inside strings
    const parts = [];
    let i = 0;
    while (i < line.length) {
      // string
      STRS.lastIndex = i;
      const sm = STRS.exec(line);
      // comment
      COMMS.lastIndex = i;
      const cm = COMMS.exec(line);
      const nextStr = sm && sm.index >= i ? sm.index : Infinity;
      const nextCom = cm && cm.index >= i ? cm.index : Infinity;
      const next = Math.min(nextStr, nextCom);
      if (next === Infinity) { parts.push({ t: "x", v: line.slice(i) }); break; }
      if (next > i) parts.push({ t: "x", v: line.slice(i, next) });
      if (next === nextCom) { parts.push({ t: "c", v: cm[0] }); i = next + cm[0].length; }
      else { parts.push({ t: "s", v: sm[0] }); i = next + sm[0].length; }
    }
    const tokenize = (text) => {
      const out = [];
      let rest = text;
      let idx = 0;
      // split by non-identifier chars keeping them
      const re = /([A-Za-z_$][\w$]*|\d+\.?\d*|[^\w\s]+|\s+)/g;
      let m;
      while ((m = re.exec(text))) {
        const tok = m[0];
        if (/^\s+$/.test(tok)) out.push({ t: "x", v: tok });
        else if (/^(const|let|var|import|from|export|default|return|async|await|function|new|if|else|true|false|null|undefined|void|class|this|as|in)$/.test(tok)) out.push({ t: "k", v: tok });
        else if (/^\d/.test(tok)) out.push({ t: "n", v: tok });
        else if (/^[A-Za-z_$][\w$]*$/.test(tok) && text.slice(re.lastIndex, re.lastIndex + 1) === "(") out.push({ t: "f", v: tok });
        else if (/^[{}()\[\];,.:=+\-*/<>!?&|]+$/.test(tok)) out.push({ t: "p", v: tok });
        else out.push({ t: "x", v: tok });
      }
      return out;
    };
    const nodes = [];
    parts.forEach((p, i) => {
      if (p.t === "s") nodes.push(<span key={i} className="tok-s">{p.v}</span>);
      else if (p.t === "c") nodes.push(<span key={i} className="tok-c">{p.v}</span>);
      else tokenize(p.v).forEach((tk, j) => {
        const cls = tk.t === "x" ? "" : `tok-${tk.t}`;
        nodes.push(<span key={`${i}-${j}`} className={cls}>{tk.v}</span>);
      });
    });
    return (
      <div key={ln}>
        <span className="ln">{ln + 1}</span>
        {nodes.length ? nodes : <span>&nbsp;</span>}
      </div>
    );
  };
  return <>{lines.map((l, i) => renderLine(l, i))}</>;
};

// --- Empty state
const Empty = ({ title, desc, actions, illus = "diagram" }) => (
  <div className="empty">
    <EmptyIllus kind={illus} />
    <div>
      <div className="empty-title">{title}</div>
      <div className="empty-desc">{desc}</div>
    </div>
    {actions && <div className="row" style={{ justifyContent: "center" }}>{actions}</div>}
  </div>
);

const EmptyIllus = ({ kind }) => {
  if (kind === "glyph") {
    return (
      <div className="empty-illus" style={{ display: "grid", placeItems: "center" }}>
        <div style={{
          width: 56, height: 56, borderRadius: 14, border: "1px dashed var(--line-strong)",
          display: "grid", placeItems: "center", color: "var(--ink-4)",
        }}>
          <IPlus size={22} />
        </div>
      </div>
    );
  }
  if (kind === "stripes") {
    return (
      <div className="empty-illus" style={{
        background: "repeating-linear-gradient(135deg, var(--bg-sunken) 0 8px, var(--bg) 8px 16px)",
        border: "1px solid var(--line)", borderRadius: 12,
      }} />
    );
  }
  // diagram (default): small schematic
  return (
    <svg className="empty-illus" viewBox="0 0 220 120" fill="none" stroke="var(--line-strong)" strokeWidth="1">
      <rect x="10" y="18" width="60" height="34" rx="4" fill="var(--bg-sunken)"/>
      <rect x="150" y="18" width="60" height="34" rx="4" fill="var(--bg-sunken)"/>
      <rect x="80" y="68" width="60" height="34" rx="4" fill="var(--bg-sunken)"/>
      <path d="M70 35 Q110 35 110 68" strokeDasharray="3 3"/>
      <path d="M150 35 Q110 35 110 68" strokeDasharray="3 3"/>
      <circle cx="40" cy="35" r="2.5" fill="var(--accent)" stroke="none"/>
      <circle cx="180" cy="35" r="2.5" fill="var(--accent)" stroke="none"/>
      <circle cx="110" cy="85" r="2.5" fill="var(--accent)" stroke="none"/>
      <text x="40" y="38" fontFamily="var(--font-mono)" fontSize="8" textAnchor="middle" fill="var(--ink-3)" stroke="none">identity</text>
      <text x="180" y="38" fontFamily="var(--font-mono)" fontSize="8" textAnchor="middle" fill="var(--ink-3)" stroke="none">wallet</text>
      <text x="110" y="88" fontFamily="var(--font-mono)" fontSize="8" textAnchor="middle" fill="var(--ink-3)" stroke="none">chain</text>
    </svg>
  );
};

// --- KV row
const KV = ({ k, children }) => (
  <div style={{ display: "grid", gridTemplateColumns: "140px 1fr", gap: 12, padding: "8px 0", borderBottom: "1px dashed var(--line)" }}>
    <div style={{ fontSize: 12.5, color: "var(--ink-3)" }}>{k}</div>
    <div style={{ fontSize: 13 }}>{children}</div>
  </div>
);

// --- Banner
const Banner = ({ kind = "info", title, children, action }) => {
  const colors = {
    info: { bg: "var(--info-soft)", fg: "var(--info)", border: "color-mix(in oklch, var(--info) 30%, var(--line))" },
    warn: { bg: "var(--warn-soft)", fg: "var(--warn)", border: "color-mix(in oklch, var(--warn) 30%, var(--line))" },
    danger: { bg: "var(--danger-soft)", fg: "var(--danger)", border: "color-mix(in oklch, var(--danger) 30%, var(--line))" },
    ok: { bg: "var(--ok-soft)", fg: "var(--ok)", border: "color-mix(in oklch, var(--ok) 30%, var(--line))" },
  }[kind];
  const Icon = kind === "danger" || kind === "warn" ? IWarn : kind === "ok" ? ICheck : IInfo;
  return (
    <div style={{
      background: colors.bg, border: `1px solid ${colors.border}`, borderRadius: 10,
      padding: "12px 14px", display: "flex", gap: 12, alignItems: "flex-start",
    }}>
      <div style={{ color: colors.fg, marginTop: 1 }}><Icon size={16}/></div>
      <div style={{ flex: 1 }}>
        {title && <div style={{ fontWeight: 500, fontSize: 13.5, color: "oklch(from " + colors.fg + " calc(l - 0.15) c h)" }}>{title}</div>}
        <div style={{ fontSize: 13, color: "var(--ink-2)", lineHeight: 1.5, marginTop: title ? 2 : 0 }}>{children}</div>
      </div>
      {action}
    </div>
  );
};

// --- Progress bar
const Progress = ({ value }) => (
  <div style={{ height: 4, background: "var(--bg-sunken)", borderRadius: 999, overflow: "hidden" }}>
    <div style={{ width: `${value}%`, height: "100%", background: "var(--accent)", transition: "width 320ms var(--ease-out)" }} />
  </div>
);

Object.assign(window, {
  Status, MonoId, Chain, Btn, Card, Field, Segmented, Toggle, CodeBlock, Highlight,
  Empty, KV, Banner, Progress,
});
