// Shared UI atoms (React) — icons, pills, avatars, progress
const { useState, useEffect, useMemo, useRef, useCallback, useLayoutEffect, createContext, useContext } = React;

// SF-Symbols inspired line icons
function Icon({ name, size=16, stroke=1.6 }) {
  const p = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round' };
  switch (name) {
    case 'search': return <svg {...p}><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>;
    case 'plus': return <svg {...p}><path d="M12 5v14M5 12h14"/></svg>;
    case 'x': return <svg {...p}><path d="M18 6 6 18M6 6l12 12"/></svg>;
    case 'check': return <svg {...p}><path d="M5 12l5 5L20 7"/></svg>;
    case 'sliders': return <svg {...p}><path d="M4 6h10M4 12h6M4 18h14M14 6h6M10 12h10M18 18h2"/><circle cx="14" cy="6" r="2"/><circle cx="10" cy="12" r="2"/><circle cx="18" cy="18" r="2"/></svg>;
    case 'settings': return <svg {...p}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 0 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 0 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 0 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 0 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z"/></svg>;
    case 'link': return <svg {...p}><path d="M10 14a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 10a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/></svg>;
    case 'gantt': return <svg {...p}><path d="M3 6h10M6 12h10M9 18h10"/></svg>;
    case 'kanban': return <svg {...p}><rect x="3" y="4" width="5" height="16" rx="1"/><rect x="10" y="4" width="5" height="10" rx="1"/><rect x="17" y="4" width="5" height="14" rx="1"/></svg>;
    case 'table': return <svg {...p}><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 10h18M3 16h18M9 4v16M15 4v16"/></svg>;
    case 'calendar': return <svg {...p}><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 10h18M8 3v4M16 3v4"/></svg>;
    case 'budget': return <svg {...p}><circle cx="12" cy="12" r="9"/><path d="M12 7v10M9 10h4a2 2 0 0 1 0 4h-4M15 14h-4"/></svg>;
    case 'flag': return <svg {...p}><path d="M5 21V4m0 0h12l-2 4 2 4H5"/></svg>;
    case 'sun': return <svg {...p}><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M2 12h2M20 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>;
    case 'moon': return <svg {...p}><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z"/></svg>;
    case 'globe': return <svg {...p}><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>;
    case 'chevron-down': return <svg {...p}><path d="m6 9 6 6 6-6"/></svg>;
    case 'chevron-right': return <svg {...p}><path d="m9 6 6 6-6 6"/></svg>;
    case 'chevron-left': return <svg {...p}><path d="m15 6-6 6 6 6"/></svg>;
    case 'trash': return <svg {...p}><path d="M4 7h16M9 7V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2M6 7l1 13a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-13"/></svg>;
    case 'comment': return <svg {...p}><path d="M21 12a8 8 0 0 1-12.3 6.8L3 20l1.3-5A8 8 0 1 1 21 12Z"/></svg>;
    case 'warning': return <svg {...p}><path d="M12 3 2 21h20L12 3zM12 10v5M12 18v.5"/></svg>;
    case 'info': return <svg {...p}><circle cx="12" cy="12" r="9"/><path d="M12 8v.5M12 12v4"/></svg>;
    case 'refresh': return <svg {...p}><path d="M3 12a9 9 0 0 1 15-6.7L21 8M21 3v5h-5M21 12a9 9 0 0 1-15 6.7L3 16M3 21v-5h5"/></svg>;
    case 'upload': return <svg {...p}><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2M12 3v13M7 8l5-5 5 5"/></svg>;
    case 'download': return <svg {...p}><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2M12 17V4M7 12l5 5 5-5"/></svg>;
    case 'external': return <svg {...p}><path d="M14 4h6v6M10 14 20 4M10 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4"/></svg>;
    case 'filter': return <svg {...p}><path d="M3 5h18l-7 9v6l-4-2v-4L3 5Z"/></svg>;
    case 'list': return <svg {...p}><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/></svg>;
    default: return null;
  }
}

function StatusPill({ status, t }) {
  const key = AOX_UTIL.statusKey(status);
  const label = t.status[status] || status;
  return <span className={'status-pill ' + key}><span className="dot"/>{label}</span>;
}
function RiskPill({ risk, t }) {
  return <span className={'risk ' + AOX_UTIL.riskKey(risk)}>{t.risk[risk] || risk}</span>;
}
function PhaseTag({ phase, color }) {
  const n = (phase||'').match(/\d/)?.[0] || '';
  const c = color || AOX_UTIL.phaseColor(phase);
  return (
    <span className="mono" style={{
      color: c,
      fontWeight: 600, fontSize: 10.5, letterSpacing: 0.04,
      textTransform: 'uppercase'
    }}>P{n}</span>
  );
}
function TrackTag({ track, workstreams }) {
  const ws = workstreams?.find(w => w.key === track);
  const fallback = track === '개발' ? 'var(--track-dev)' : track === 'PR·마케팅' ? 'var(--track-prm)' : track === '운영' ? 'var(--track-ops)' : '#999';
  const color = ws ? ws.color : fallback;
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', gap:4,
      fontSize: 11, color: color, fontWeight: 500
    }}>
      <span style={{width:6, height:6, borderRadius:'50%', background: color}}/>
      {track}
    </span>
  );
}
function Avatar({ person, size=22 }) {
  if (!person) return (
    <span className="avatar" style={{width:size, height:size, background:'var(--surface-2)', color:'var(--fg-muted)', border:'1px dashed var(--border-strong)'}}>?</span>
  );
  return (
    <span className="avatar" style={{width:size, height:size, background: person.color, fontSize: size*0.44}}>
      {AOX_UTIL.initials(person.name)}
    </span>
  );
}
function Progress({ value }) {
  return (
    <div className="progress-bar" style={{width: 64}}>
      <div className={'progress-fill' + (value>=100 ? ' done' : '')} style={{width: Math.min(100, value||0) + '%'}}/>
    </div>
  );
}

// Small search-suggest dropdown
function Select({ value, onChange, options, placeholder }) {
  return (
    <select value={value||''} onChange={e=>onChange(e.target.value)} style={{
      padding:'5px 8px', borderRadius:8, border:'1px solid var(--border)',
      background:'var(--surface)', fontSize:12.5
    }}>
      {placeholder && <option value="">{placeholder}</option>}
      {options.map(o => <option key={o.value ?? o} value={o.value ?? o}>{o.label ?? o}</option>)}
    </select>
  );
}

// AOX brand wordmark — PNG asset, theme-aware.
//   size: 'sm' (topbar ~20px) | 'md' (28px) | 'lg' (login ~44px)
function AoxLogo({ size = 'md' }) {
  const [theme, setTheme] = useState(() => document.documentElement.dataset.theme || '');
  useEffect(() => {
    const el = document.documentElement;
    const obs = new MutationObserver(() => setTheme(el.dataset.theme || ''));
    obs.observe(el, { attributes: true, attributeFilter: ['data-theme'] });
    return () => obs.disconnect();
  }, []);
  const heights = { sm: 20, md: 28, lg: 44 };
  const src = theme === 'dark' ? '/assets/aox-logo-white.png' : '/assets/aox-logo-black.png';
  return (
    <img
      src={src}
      alt="AOX"
      style={{ height: heights[size] || 28, width: 'auto', display: 'block' }}
      draggable={false}
    />
  );
}

// Odometer-style rolling number. Each mount animates from random digits to
// `target`, locking left-to-right (hundreds first, then tens, then ones) with
// ease-out-cubic over `duration`, then a 200ms bounce on the final frame.
// prefers-reduced-motion → render the target directly.
function RollingNumber({ target, duration = 800, delay = 0 }) {
  const safeTarget = Math.max(0, Math.floor(Number(target) || 0));
  const reduceMotion = typeof window !== 'undefined' &&
    window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const [display, setDisplay] = useState(reduceMotion ? safeTarget : 0);
  const [bounce, setBounce] = useState(false);
  const ranRef = useRef(false);

  useEffect(() => {
    if (reduceMotion) { setDisplay(safeTarget); return; }
    // Animate on every mount + target change (user wants re-play on tab switch).
    ranRef.current = false;
    let raf = 0;
    const start = performance.now() + delay;
    const targetStr = String(safeTarget);
    const digits = Math.max(1, targetStr.length);
    const padded = targetStr.padStart(digits, '0');

    const tick = (now) => {
      const elapsed = now - start;
      if (elapsed < 0) { raf = requestAnimationFrame(tick); return; }
      const progress = Math.min(elapsed / duration, 1);
      const eased = 1 - Math.pow(1 - progress, 3); // ease-out-cubic
      let out = '';
      for (let i = 0; i < digits; i++) {
        // Lock left-to-right: digit i settles when eased >= (i+1)/digits.
        const lockAt = (i + 1) / digits;
        out += eased >= lockAt ? padded[i] : String(Math.floor(Math.random() * 10));
      }
      setDisplay(Number(out));
      if (progress < 1) {
        raf = requestAnimationFrame(tick);
      } else if (!ranRef.current) {
        ranRef.current = true;
        setDisplay(safeTarget);
        setBounce(true);
        setTimeout(() => setBounce(false), 220);
      }
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [safeTarget, duration, delay, reduceMotion]);

  return (
    <span className={'num-tab' + (bounce ? ' rolling-bounce' : '')} style={{display:'inline-block'}}>
      {display}
    </span>
  );
}

Object.assign(window, { Icon, StatusPill, RiskPill, PhaseTag, TrackTag, Avatar, Progress, Select, AoxLogo, RollingNumber, useState, useEffect, useMemo, useRef, useCallback, useLayoutEffect });
