/* global React */
// ============================================================
// Shared components — icons, badges, meters, chips, shell
// ============================================================
const { useState, useEffect, useMemo, useRef, createContext, useContext } = React;

// ============== ICONS — original, neutral, geometric ==============
const Icon = {
  // app mark (faceted gem — original, not SE assets)
  Mark: () => (
    <svg viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinejoin="round">
      <path d="M12 3 L20 9 L17 20 L7 20 L4 9 Z" />
      <path d="M12 3 L12 20 M4 9 L20 9 M8 9 L12 20 L16 9" opacity="0.55" />
    </svg>
  ),
  // nav
  Dashboard: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="2" y="2" width="5.5" height="6.5" rx="1"/><rect x="2" y="10" width="5.5" height="4" rx="1"/><rect x="8.5" y="2" width="5.5" height="4" rx="1"/><rect x="8.5" y="7.5" width="5.5" height="6.5" rx="1"/></svg>,
  Collections: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="2" y="2" width="5" height="5" rx="0.8"/><rect x="9" y="2" width="5" height="5" rx="0.8"/><rect x="2" y="9" width="5" height="5" rx="0.8"/><rect x="9" y="9" width="5" height="5" rx="0.8"/></svg>,
  Relic: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"><path d="M8 1.5 L13.5 6 L11 14.5 L5 14.5 L2.5 6 Z"/><path d="M8 1.5 L8 14.5 M2.5 6 L13.5 6" opacity="0.55"/></svg>,
  Static: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="5" cy="6" r="2"/><circle cx="11" cy="6" r="2"/><path d="M2 14 c0-2 1.5-3.5 3-3.5 s3 1.5 3 3.5"/><path d="M8 14 c0-2 1.5-3.5 3-3.5 s3 1.5 3 3.5"/></svg>,
  FC: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"><path d="M2 14 L2 6 L8 2 L14 6 L14 14 Z"/><path d="M6 14 L6 9 L10 9 L10 14"/></svg>,
  Settings: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="8" cy="8" r="2.2"/><path d="M8 1.5 L8 3 M8 13 L8 14.5 M1.5 8 L3 8 M13 8 L14.5 8 M3.3 3.3 L4.4 4.4 M11.6 11.6 L12.7 12.7 M3.3 12.7 L4.4 11.6 M11.6 4.4 L12.7 3.3"/></svg>,
  // utility
  Search: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5 L14 14" strokeLinecap="round"/></svg>,
  Refresh: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><path d="M2 8 a6 6 0 0 1 10.5 -4 M14 8 a6 6 0 0 1 -10.5 4"/><path d="M12.5 1.5 L12.5 4.5 L9.5 4.5 M3.5 14.5 L3.5 11.5 L6.5 11.5"/></svg>,
  Check: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8.5 L7 12 L13 4"/></svg>,
  X: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M3.5 3.5 L12.5 12.5 M12.5 3.5 L3.5 12.5"/></svg>,
  Plus: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M8 3 L8 13 M3 8 L13 8"/></svg>,
  ChevR: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M6 3 L11 8 L6 13"/></svg>,
  ChevL: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M10 3 L5 8 L10 13"/></svg>,
  ChevD: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6 L8 11 L13 6"/></svg>,
  ChevUD: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 6 L8 3 L11 6 M5 10 L8 13 L11 10"/></svg>,
  More: () => <svg viewBox="0 0 16 16" fill="currentColor"><circle cx="3" cy="8" r="1.4"/><circle cx="8" cy="8" r="1.4"/><circle cx="13" cy="8" r="1.4"/></svg>,
  Copy: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="5" y="5" width="9" height="9" rx="1.5"/><path d="M3 11 L3 3 a1 1 0 0 1 1 -1 L11 2"/></svg>,
  Edit: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><path d="M11 2.5 L13.5 5 L5 13.5 L2 14 L2.5 11 Z"/></svg>,
  Discord: () => <svg viewBox="0 0 24 24" fill="currentColor"><path d="M19.27 5.33C17.94 4.71 16.5 4.26 15 4a.09.09 0 0 0-.07.03c-.18.33-.39.76-.53 1.09a16.09 16.09 0 0 0-4.8 0c-.14-.34-.35-.76-.54-1.09a.1.1 0 0 0-.07-.03 13.86 13.86 0 0 0-4.27 1.33.06.06 0 0 0-.03.03c-2.73 4.08-3.48 8.06-3.1 11.98 0 .02.01.04.03.05 1.8 1.32 3.53 2.12 5.24 2.65a.1.1 0 0 0 .08-.01c.4-.55.76-1.13 1.07-1.74a.07.07 0 0 0-.04-.1 9.22 9.22 0 0 1-1.32-.63.07.07 0 0 1-.01-.11 5.5 5.5 0 0 0 .26-.2.06.06 0 0 1 .07-.01c2.76 1.26 5.74 1.26 8.47 0 .02-.01.05 0 .07.01.08.07.17.14.26.21.04.03.04.1-.01.11-.42.25-.86.45-1.32.63a.07.07 0 0 0-.04.1c.32.61.68 1.19 1.07 1.73.02.02.05.03.08.02 1.72-.53 3.45-1.33 5.25-2.65a.07.07 0 0 0 .03-.05c.46-4.53-.76-8.48-3.21-11.98a.05.05 0 0 0-.03-.03zM8.52 14.65c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.91.96 1.89 2.12 0 1.17-.84 2.12-1.89 2.12zm6.97 0c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.91.96 1.89 2.12 0 1.17-.83 2.12-1.89 2.12z"/></svg>,
  Sun: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><circle cx="8" cy="8" r="2.5"/><path d="M8 1.5v1.5M8 13v1.5M14.5 8H13M3 8H1.5M12.7 3.3l-1.1 1.1M4.4 11.6l-1.1 1.1M12.7 12.7l-1.1-1.1M4.4 4.4 L3.3 3.3"/></svg>,
  Moon: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M13 9.5 A6 6 0 1 1 6.5 3 A5 5 0 0 0 13 9.5 Z"/></svg>,
  External: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><path d="M6 3 L3 3 L3 13 L13 13 L13 10 M9 3 L13 3 L13 7 M8 8 L13 3"/></svg>,
  Sidebar: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="2" y="3" width="12" height="10" rx="1.5"/><path d="M6 3 L6 13"/></svg>,
  Warning: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M8 2 L14.5 13 L1.5 13 Z"/><path d="M8 6 L8 9 M8 11 L8 11.5"/></svg>,
  Info: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="8" cy="8" r="6.5"/><path d="M8 7 L8 11.5 M8 4.5 L8 5" strokeLinecap="round"/></svg>,
  Lock: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5 7 L5 5 a3 3 0 0 1 6 0 L11 7"/></svg>,
  Link: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"><path d="M7 9 L9 7 M6 11 L4 11 a3 3 0 0 1 0 -6 L7 5 M9 5 L12 5 a3 3 0 0 1 0 6 L10 11"/></svg>,
  Bot: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="2.5" y="5" width="11" height="8" rx="2"/><circle cx="6" cy="9" r="1" fill="currentColor"/><circle cx="10" cy="9" r="1" fill="currentColor"/><path d="M8 5 L8 3" strokeLinecap="round"/><circle cx="8" cy="2.5" r="0.7" fill="currentColor"/></svg>,
  Loot: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"><path d="M2 6 L8 2.5 L14 6 L14 13 L2 13 Z"/><path d="M2 6 L14 6 M8 2.5 L8 13"/></svg>,
  Clock: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="8" cy="8" r="6.5"/><path d="M8 4.5 L8 8 L10.5 9.5" strokeLinecap="round"/></svg>,
  Filter: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M2 3 L14 3 L10 8.5 L10 13 L6 13 L6 8.5 Z"/></svg>,
  Spinner: () => <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" style={{animation: 'spin 0.9s linear infinite'}}><path d="M8 1.5 A6.5 6.5 0 1 1 1.5 8" /></svg>,
};

// ============== role / job helpers ==============
const ROLES = {
  PLD: { role: 'tank', glyph: 'P' }, WAR: { role: 'tank', glyph: 'W' }, DRK: { role: 'tank', glyph: 'D' }, GNB: { role: 'tank', glyph: 'G' },
  WHM: { role: 'heal', glyph: 'W' }, SCH: { role: 'heal', glyph: 'S' }, AST: { role: 'heal', glyph: 'A' }, SGE: { role: 'heal', glyph: 'S' },
  MNK: { role: 'dps', glyph: 'M' }, DRG: { role: 'dps', glyph: 'D' }, NIN: { role: 'dps', glyph: 'N' }, SAM: { role: 'dps', glyph: 'S' }, RPR: { role: 'dps', glyph: 'R' }, VPR: { role: 'dps', glyph: 'V' },
  BRD: { role: 'dps', glyph: 'B' }, MCH: { role: 'dps', glyph: 'M' }, DNC: { role: 'dps', glyph: 'D' },
  BLM: { role: 'dps', glyph: 'B' }, SMN: { role: 'dps', glyph: 'S' }, RDM: { role: 'dps', glyph: 'R' }, PCT: { role: 'dps', glyph: 'P' },
};
function roleOf(job) { return ROLES[job]?.role || 'dps'; }
function glyphOf(job) { return ROLES[job]?.glyph || job[0]; }

function RoleChip({ job }) {
  const r = roleOf(job);
  return (
    <span className={`role-chip ${r}`}>
      <span className="glyph">{glyphOf(job)}</span>
      {job}
    </span>
  );
}
function JobChip({ job, lvl, cap }) {
  const r = roleOf(job);
  return (
    <span className={`job-chip ${r} ${cap ? 'cap' : ''}`}>
      <span className="glyph">{glyphOf(job)}</span>
      {job} <span className="lvl">{lvl}</span>
    </span>
  );
}

// ============== Origin badge ==============
function Origin({ kind, label }) {
  const labels = { auto: 'auto', manual: 'manual', diffed: 'diffed' };
  return (
    <span className={`origin ${kind}`} title={`Data origin: ${kind}`}>
      <span className="dot"></span>
      {label || labels[kind]}
    </span>
  );
}

// ============== Sync chip ==============
function SyncChip({ state = 'ok', label }) {
  const labels = {
    ok: label || 'Synced 2h ago',
    syncing: 'Syncing…',
    failed: 'Sync failed',
  };
  return (
    <button className={`sync-chip ${state === 'syncing' ? 'syncing' : ''} ${state === 'failed' ? 'failed' : ''}`}>
      <span className="dot"></span>
      <span>{labels[state]}</span>
      <Icon.Refresh />
    </button>
  );
}

// ============== Progress meter ==============
function Meter({ value, total, label, size = 'md', tone, showPct = true, suffix }) {
  const pct = total ? Math.round((value / total) * 100) : value;
  // tone scaling: red -> amber -> green
  let color = 'var(--primary)';
  if (tone === 'status') {
    if (pct < 35) color = 'var(--danger)';
    else if (pct < 75) color = 'var(--warning)';
    else color = 'var(--success)';
  } else if (tone) {
    color = `var(--${tone})`;
  }
  return (
    <div className={`meter ${size === 'lg' ? 'lg' : size === 'sm' ? 'sm' : ''}`} style={{ '--val': pct, '--color': color }}>
      {(label || total != null) && (
        <div className="meter-row">
          {label && <div className="meter-label"><span className="nm">{label}</span></div>}
          <div className="meter-vals">
            {total != null && <><strong>{value}</strong> / {total}</>}
            {showPct && total != null && <span style={{ marginLeft: 8 }}>{pct}%</span>}
            {showPct && total == null && <strong>{pct}%</strong>}
            {suffix}
          </div>
        </div>
      )}
      <div className="meter-bar"></div>
    </div>
  );
}

// ============== Checkbox ==============
function CB({ on, onChange }) {
  return (
    <button className={`cb ${on ? 'on' : ''}`} onClick={(e) => { e.stopPropagation(); onChange?.(!on); }}>
      <Icon.Check />
    </button>
  );
}

// ============== Portrait placeholder ==============
function Portrait({ size = 56, label = 'character' }) {
  return <div className="portrait" style={{ width: size, height: size }} data-label={label}></div>;
}

// ============== Tag ==============
function Tag({ children, kind, dot }) {
  return <span className={`tag ${kind || ''} ${dot ? 'dot' : ''}`}>{children}</span>;
}

// ============== Empty state ==============
function Empty({ icon, title, sub, action }) {
  return (
    <div className="empty">
      <div className="empty-ic">{icon}</div>
      <h4 className="empty-title">{title}</h4>
      {sub && <p className="empty-sub">{sub}</p>}
      {action}
    </div>
  );
}

// ============== Segmented control ==============
function Seg({ options, value, onChange }) {
  return (
    <div className="seg">
      {options.map(o => (
        <button key={o.value} className={value === o.value ? 'on' : ''} onClick={() => onChange?.(o.value)}>
          {o.label}
        </button>
      ))}
    </div>
  );
}

// ============== Input ==============
function Input({ icon, ...rest }) {
  if (!icon) return <input className="input" {...rest} />;
  return (
    <span className="input-wrap">
      <span className="lead">{icon}</span>
      <input className="input" {...rest} />
    </span>
  );
}

// ============== Wordmark — Crystaltide ==============
// One word, sentence case. "Crystal" is full-weight, "tide" relaxes to a lighter
// weight + slightly softer color so the duality reads in the type itself,
// without ever splitting the word with a mid-word capital. The faceted-gem mark
// optionally sits alongside.
function Wordmark({ size = 14, withMark = true, accent = false, style }) {
  const px = typeof size === 'number' ? `${size}px` : size;
  return (
    <span
      className="wordmark"
      style={{ fontSize: px, gap: Math.max(6, Math.round((parseFloat(px) || 14) * 0.42)), ...style }}
    >
      {withMark && (
        <span
          className="wordmark-mark"
          style={{ width: `${parseFloat(px) * 1.05}px`, height: `${parseFloat(px) * 1.05}px` }}
        >
          <Icon.Mark />
        </span>
      )}
      <span className="wordmark-text">
        <span className="wm-crystal">Crystal</span>
        <span className={`wm-tide ${accent ? 'accent' : ''}`}>tide</span>
      </span>
    </span>
  );
}

// expose
Object.assign(window, {
  Icon, ROLES, roleOf, glyphOf,
  RoleChip, JobChip, Origin, SyncChip, Meter, CB, Portrait, Tag, Empty, Seg, Input,
  Wordmark,
});
