/* global React */
// Shared primitives: buttons, badges, icons, ref chips, sigil, sparkline, KPI

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

// ─── Deterministic hash → generative sigil ───────────────────
function hashStr(s) {
  let h = 2166136261 >>> 0;
  for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); }
  return h >>> 0;
}

function Sigil({ seed, size = 28 }) {
  // 5x5 symmetric glyph deterministic from seed
  const h = hashStr(seed);
  const cells = [];
  for (let y = 0; y < 5; y++) {
    for (let x = 0; x < 3; x++) {
      const bit = (h >> (y * 3 + x)) & 1;
      cells.push({ x, y, on: bit });
      if (x < 2) cells.push({ x: 4 - x, y, on: bit });
    }
  }
  const unit = size / 5;
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ display: 'block', borderRadius: 3, background: 'var(--paper-2)' }}>
      {cells.map((c, i) => c.on ? (
        <rect key={i} x={c.x * unit + 0.5} y={c.y * unit + 0.5} width={unit - 1} height={unit - 1} fill="var(--ink)" />
      ) : null)}
    </svg>
  );
}

// ─── Ref chip (monospace, copyable) ──────────────────────────
function RefChip({ r, onClick }) {
  const [copied, setCopied] = useState(false);
  return (
    <button
      onClick={(e) => {
        e.stopPropagation();
        navigator.clipboard?.writeText(r);
        setCopied(true);
        setTimeout(() => setCopied(false), 1200);
        onClick && onClick();
      }}
      className="mono"
      title={copied ? 'Copié' : 'Cliquer pour copier'}
      style={{
        fontSize: 11, padding: '2px 7px',
        background: 'var(--paper-2)',
        border: '1px solid var(--line)',
        borderRadius: 3,
        color: 'var(--ink-2)',
        letterSpacing: '-0.01em',
        transition: 'all 120ms',
      }}
      onMouseEnter={e => e.currentTarget.style.background = 'var(--paper-3)'}
      onMouseLeave={e => e.currentTarget.style.background = 'var(--paper-2)'}
    >
      {copied ? '✓ copié' : r}
    </button>
  );
}

// ─── Status dot ──────────────────────────────────────────────
const STATUS_COLORS = {
  signal: 'var(--signal)',
  copper: 'var(--copper)',
  amber: 'var(--amber)',
  plasma: 'var(--plasma)',
  rouge: 'var(--rouge)',
  muted: 'var(--ink-4)',
};
function Dot({ tone = 'signal', pulse = false, size = 8 }) {
  return (
    <span style={{
      display: 'inline-block',
      width: size, height: size, borderRadius: '50%',
      background: STATUS_COLORS[tone] || tone,
      animation: pulse ? 'dot-pulse 2s infinite' : 'none',
      flexShrink: 0,
    }} />
  );
}

// ─── Badge ───────────────────────────────────────────────────
function Badge({ children, tone = 'neutral', variant = 'solid', icon }) {
  const toneMap = {
    neutral: { bg: 'var(--paper-2)', fg: 'var(--ink-2)', bd: 'var(--line-2)' },
    signal:  { bg: 'var(--signal-tint)', fg: 'var(--signal-deep)', bd: 'var(--signal-soft)' },
    copper:  { bg: 'var(--copper-tint)', fg: 'var(--copper)', bd: 'var(--copper-tint)' },
    amber:   { bg: 'rgba(232,179,65,0.14)', fg: '#8C6510', bd: 'rgba(232,179,65,0.3)' },
    plasma:  { bg: 'var(--plasma-tint)', fg: 'var(--plasma)', bd: 'var(--plasma-tint)' },
    rouge:   { bg: 'var(--rouge-tint)', fg: 'var(--rouge)', bd: 'var(--rouge-tint)' },
    ink:     { bg: 'var(--ink)', fg: 'var(--paper)', bd: 'var(--ink)' },
  };
  const t = toneMap[tone] || toneMap.neutral;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      fontSize: 11, fontWeight: 500,
      padding: '3px 8px',
      background: t.bg, color: t.fg,
      border: `1px solid ${t.bd}`,
      borderRadius: 999,
      letterSpacing: 0.1,
      whiteSpace: 'nowrap',
    }}>
      {icon} {children}
    </span>
  );
}

// ─── Button ──────────────────────────────────────────────────
function Btn({ children, variant = 'ghost', size = 'md', onClick, icon, iconRight, kbd, style, disabled, tone }) {
  const sizes = {
    sm: { pad: '4px 10px', fs: 12, h: 26 },
    md: { pad: '6px 12px', fs: 13, h: 32 },
    lg: { pad: '10px 16px', fs: 14, h: 40 },
  }[size];
  const base = {
    display: 'inline-flex', alignItems: 'center', gap: 8,
    fontSize: sizes.fs, fontWeight: 500,
    padding: sizes.pad, height: sizes.h,
    borderRadius: 6, letterSpacing: 0,
    transition: 'all 120ms cubic-bezier(.2,.8,.2,1)',
    cursor: disabled ? 'not-allowed' : 'pointer',
    opacity: disabled ? 0.5 : 1,
  };
  const variants = {
    primary: { ...base, background: 'var(--ink)', color: 'var(--paper)', border: '1px solid var(--ink)' },
    signal:  { ...base, background: 'var(--signal)', color: '#0B1F12', border: '1px solid var(--signal)', fontWeight: 600 },
    outline: { ...base, background: 'transparent', color: 'var(--ink)', border: '1px solid var(--line-2)' },
    ghost:   { ...base, background: 'transparent', color: 'var(--ink-2)', border: '1px solid transparent' },
    danger:  { ...base, background: 'var(--rouge-tint)', color: 'var(--rouge)', border: '1px solid var(--rouge-tint)' },
  };
  const hover = {
    primary: { background: 'var(--ink-2)' },
    signal:  { background: 'var(--signal-deep)', color: '#fff' },
    outline: { background: 'var(--paper-2)' },
    ghost:   { background: 'var(--paper-2)' },
    danger:  { background: 'rgba(217,70,70,0.18)' },
  };
  return (
    <button
      onClick={disabled ? undefined : onClick}
      disabled={disabled}
      style={{ ...(variants[variant] || variants.ghost), ...style }}
      onMouseEnter={e => !disabled && Object.assign(e.currentTarget.style, hover[variant] || hover.ghost)}
      onMouseLeave={e => !disabled && Object.assign(e.currentTarget.style, variants[variant] || variants.ghost, style)}
    >
      {icon}{children}{iconRight}
      {kbd && <span className="mono" style={{ fontSize: 10, opacity: 0.6, padding: '1px 4px', border: '1px solid currentColor', borderRadius: 3, marginLeft: 4 }}>{kbd}</span>}
    </button>
  );
}

// ─── Simple SVG icons (1.5px line) ───────────────────────────
const Icon = {
  search: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><circle cx="7" cy="7" r="5"/><path d="m11 11 3 3"/></svg>,
  command: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M4.5 2a2.5 2.5 0 1 1 0 5H2v7a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7h-2.5a2.5 2.5 0 1 1 0-5"/></svg>,
  arrow: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M3 8h10m-4-4 4 4-4 4"/></svg>,
  check: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.8" {...p}><path d="m3 8 3 3 7-7"/></svg>,
  cross: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="m4 4 8 8M12 4l-8 8"/></svg>,
  plus: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 3v10M3 8h10"/></svg>,
  filter: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 3h12l-4.5 5.5V13l-3 1V8.5z"/></svg>,
  more: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor" {...p}><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>,
  bolt: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M9 2 3 9h4l-1 5 6-7H8z"/></svg>,
  camera: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 5h3l1-1.5h4L11 5h3v8H2z"/><circle cx="8" cy="9" r="2.5"/></svg>,
  mic: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><rect x="6" y="2" width="4" height="7" rx="2"/><path d="M4 8a4 4 0 0 0 8 0M8 12v2M5 14h6"/></svg>,
  spark: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 2v2m0 8v2M2 8h2m8 0h2M4 4l1.5 1.5M10.5 10.5 12 12M4 12l1.5-1.5M10.5 5.5 12 4"/></svg>,
  doc: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M3 2h7l3 3v9H3z"/><path d="M10 2v3h3"/></svg>,
  sign: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 13c2-6 5-6 6-2s2 2 3-1 3-1 3 0"/></svg>,
  map: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 4 6 2l4 2 4-2v10l-4 2-4-2-4 2z M6 2v12 M10 4v12"/></svg>,
  kanban: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 2h3v12H2zM6.5 2h3v8h-3zM11 2h3v6h-3z"/></svg>,
  home: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 8 8 2l6 6v6H2z"/></svg>,
  folder: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 4h4l1 1h7v8H2z"/></svg>,
  book: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 3h5c1 0 2 .5 2 1.5V14c0-1-1-1.5-2-1.5H2zM14 3H9c-1 0-2 .5-2 1.5V14c0-1 1-1.5 2-1.5h5z"/></svg>,
  chart: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M2 14V2m0 12h12M5 11V8m3 3V5m3 6V7"/></svg>,
  people: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><circle cx="6" cy="5" r="2.5"/><path d="M2 14c.5-2.5 2-4 4-4s3.5 1.5 4 4"/><circle cx="12" cy="6" r="2"/><path d="M10 10c3 0 4 2 4 4"/></svg>,
  settings: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><circle cx="8" cy="8" r="2"/><path d="M8 1v2m0 10v2M1 8h2m10 0h2M3 3l1.5 1.5M11.5 11.5 13 13M3 13l1.5-1.5M11.5 4.5 13 3"/></svg>,
  copilot: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><circle cx="8" cy="8" r="3"/><circle cx="8" cy="8" r="6" strokeDasharray="2 2"/></svg>,
  lock: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><rect x="3" y="7" width="10" height="7"/><path d="M5 7V5a3 3 0 0 1 6 0v2"/></svg>,
  shield: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 2 3 4v4c0 3 2 5.5 5 6 3-.5 5-3 5-6V4z"/></svg>,
  eye: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M1 8s2.5-5 7-5 7 5 7 5-2.5 5-7 5-7-5-7-5"/><circle cx="8" cy="8" r="2"/></svg>,
  refresh: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M14 3v4h-4M2 13v-4h4"/><path d="M4 7a5 5 0 0 1 9-2M12 9a5 5 0 0 1-9 2"/></svg>,
  flame: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 14c-3 0-5-2-5-5 0-2 1-3 2-4 0 2 1 2 2 1-1-2 0-4 3-4 0 3 3 4 3 7 0 3-2 5-5 5z"/></svg>,
  sun: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><circle cx="8" cy="8" r="3"/><path d="M8 1.5v1.5M8 13v1.5M1.5 8h1.5M13 8h1.5M3.3 3.3l1.1 1.1M11.6 11.6l1.1 1.1M3.3 12.7l1.1-1.1M11.6 4.4l1.1-1.1"/></svg>,
  download: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 2v9m-3-3 3 3 3-3M2 13h12"/></svg>,
  layers: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 2 2 5l6 3 6-3zM2 11l6 3 6-3M2 8l6 3 6-3"/></svg>,
  bell: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M8 2a4 4 0 0 1 4 4v3l1 1v1H3v-1l1-1V6a4 4 0 0 1 4-4z"/><path d="M6.5 13a1.5 1.5 0 0 0 3 0"/></svg>,
  calendar: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><rect x="2" y="3" width="12" height="11" rx="1.5"/><path d="M2 7h12M5 2v2M11 2v2"/></svg>,
  calendarPlus: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><path d="M10 14H3.5A1.5 1.5 0 0 1 2 12.5V5A1.5 1.5 0 0 1 3.5 3.5h9A1.5 1.5 0 0 1 14 5v4"/><path d="M2 7.5h12M5 2v3M11 2v3M12 11v4M10 13h4"/></svg>,
  clockFill: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" {...p}><circle cx="8" cy="8" r="6"/><path d="M8 5v3.5l2 1.5"/></svg>,
};

// ─── Sparkline ───────────────────────────────────────────────
function Sparkline({ data, width = 80, height = 20, stroke = 'currentColor', fill = 'none', strokeWidth = 1.5 }) {
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const pts = data.map((v, i) => {
    const x = (i / (data.length - 1)) * width;
    const y = height - ((v - min) / range) * (height - 2) - 1;
    return `${x.toFixed(1)},${y.toFixed(1)}`;
  }).join(' ');
  const area = `M0,${height} L${pts.split(' ').join(' L')} L${width},${height} Z`;
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{ display: 'block' }}>
      {fill !== 'none' && <path d={area} fill={fill} />}
      <polyline points={pts} fill="none" stroke={stroke} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// Bar sparkline
function BarSpark({ data, width = 80, height = 20, fg = 'var(--signal)', bg = 'var(--line)' }) {
  const max = Math.max(...data) || 1;
  const w = width / data.length;
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{ display: 'block' }}>
      {data.map((v, i) => {
        const h = (v / max) * (height - 1);
        return <rect key={i} x={i * w + 1} y={height - h} width={w - 2} height={h} fill={fg} rx="0.5" />;
      })}
    </svg>
  );
}

// Animated number
function AnimNum({ value, format = (n) => n, duration = 600 }) {
  const [cur, setCur] = useState(value);
  const prev = useRef(value);
  useEffect(() => {
    if (prev.current === value) return;
    const start = performance.now();
    const from = prev.current, to = value;
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setCur(from + (to - from) * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
      else prev.current = to;
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [value, duration]);
  return <>{format(cur)}</>;
}

function fmtMWh(n) {
  if (n >= 1e6) return (n / 1e6).toFixed(2) + ' GWh';
  if (n >= 1e3) return (n / 1e3).toFixed(1) + ' MWh';
  return Math.round(n).toLocaleString('fr-FR') + ' kWh';
}
function fmtEur(n) {
  if (n >= 1e6) return (n / 1e6).toFixed(2) + ' M€';
  if (n >= 1e3) return (n / 1e3).toFixed(1) + ' k€';
  return Math.round(n).toLocaleString('fr-FR') + ' €';
}
function fmtInt(n) { return Math.round(n).toLocaleString('fr-FR'); }
function fmtPct(n) { return n.toFixed(1) + ' %'; }

Object.assign(window, {
  hashStr, Sigil, RefChip, Dot, Badge, Btn, Icon,
  Sparkline, BarSpark, AnimNum,
  fmtMWh, fmtEur, fmtInt, fmtPct,
});
