// Shared UI primitives: toast notifications, an expiry-countdown helper, and
// the browser-tab pending badge. Loaded after data.jsx so every view can call
// window.toast(...) and window.fmtExpiresIn(...). Mounted once via <ToastHost/>
// and <PendingTabBadge/> in the app shell (index.html).

// ---- Expiry countdown ----------------------------------------------------
// Pending Tommy approvals auto-expire 24h after creation (renewed by
// escalate()). Surface "expires in Xh" with an urgency level so staff attack
// the ones about to die — the queue's ~80% expiry rate is the real problem.
// level: 'normal' | 'soon' (<8h) | 'critical' (<3h) | 'expired'.
function fmtExpiresIn(expiresAt) {
  const d = parseDate(expiresAt);
  if (!d) return null;
  const ms = d.getTime() - Date.now();
  if (ms <= 0) return { text: 'expired', level: 'expired', minutes: 0 };
  const minutes = Math.round(ms / 60000);
  if (minutes < 60) return { text: `expires in ${minutes}m`, level: 'critical', minutes };
  const hours = Math.floor(minutes / 60);
  const level = hours < 3 ? 'critical' : hours < 8 ? 'soon' : 'normal';
  return { text: `expires in ${hours}h`, level, minutes };
}

// Sort helper: soonest-to-expire first, nulls last. Pure; returns a new array.
function sortByExpiry(list) {
  const key = a => {
    const d = parseDate(a && a.expires_at);
    return d ? d.getTime() : Infinity;
  };
  return [...(list || [])].sort((a, b) => key(a) - key(b));
}

// ---- Toasts --------------------------------------------------------------
// Non-blocking replacement for alert()/confirm(). Fire from anywhere with
// window.toast(msg, { kind, action: { label, onClick }, duration }).
// An `action` (e.g. Undo) keeps the toast up longer.
let _toastSeq = 0;
function toast(message, opts = {}) {
  const id = ++_toastSeq;
  window.dispatchEvent(new CustomEvent('raw:toast', {
    detail: { id, message, kind: opts.kind || 'info', action: opts.action, duration: opts.duration },
  }));
  return id;
}

function ToastHost() {
  const [toasts, setToasts] = React.useState([]);
  const timers = React.useRef({});

  const remove = React.useCallback((id) => {
    if (timers.current[id]) { clearTimeout(timers.current[id]); delete timers.current[id]; }
    setToasts(prev => prev.filter(t => t.id !== id));
  }, []);

  React.useEffect(() => {
    function onToast(e) {
      const t = e.detail;
      setToasts(prev => [...prev, t]);
      const duration = t.duration || (t.action ? 6000 : 4000);
      timers.current[t.id] = setTimeout(() => remove(t.id), duration);
    }
    window.addEventListener('raw:toast', onToast);
    return () => {
      window.removeEventListener('raw:toast', onToast);
      Object.values(timers.current).forEach(clearTimeout);
    };
  }, [remove]);

  function fireAction(t) {
    try { t.action && t.action.onClick && t.action.onClick(); } catch (err) { /* no-op */ }
    remove(t.id);
  }

  if (toasts.length === 0) return null;
  return (
    <div className="toast-host" role="status" aria-live="polite">
      {toasts.map(t => (
        <div key={t.id} className={'toast toast-' + (t.kind || 'info')}>
          <span className="toast-msg">{t.message}</span>
          {t.action && (
            <button className="toast-action" onClick={() => fireAction(t)}>{t.action.label}</button>
          )}
          <button className="toast-close" onClick={() => remove(t.id)} aria-label="Dismiss">×</button>
        </div>
      ))}
    </div>
  );
}

// ---- Browser-tab pending badge ------------------------------------------
// Prefix the tab title with the count of pending Tommy approvals so staff
// working in another tab still notice the queue. Refreshes on realtime
// events and a slow 60s poll as a safety net.
function PendingTabBadge() {
  const baseTitle = React.useRef(document.title.replace(/^\(\d+\)\s*/, ''));
  React.useEffect(() => {
    let alive = true;
    async function refresh() {
      try {
        const r = await api.get('/api/v2/tommy/pending-approvals');
        if (!alive) return;
        const n = (r && r.approvals ? r.approvals : []).length;
        document.title = n > 0 ? `(${n}) ${baseTitle.current}` : baseTitle.current;
      } catch (e) { /* leave the title as-is on error */ }
    }
    refresh();
    function onEv() { refresh(); }
    window.addEventListener('raw:event', onEv);
    const iv = setInterval(refresh, 60000);
    return () => {
      alive = false;
      window.removeEventListener('raw:event', onEv);
      clearInterval(iv);
      document.title = baseTitle.current;
    };
  }, []);
  return null;
}

// ---- Skeleton loader ----
// Lightweight shimmer placeholder for loading states (replaces bare "Loading…").
function Skeleton({ w, h, r, style }) {
  return (
    <span
      className="skeleton"
      style={{ display: 'block', width: w || '100%', height: h || 12, borderRadius: r != null ? r : 4, ...(style || {}) }}
      aria-hidden="true"
    />
  );
}

Object.assign(window, { fmtExpiresIn, sortByExpiry, toast, ToastHost, PendingTabBadge, Skeleton });
