/* Dump — ui.jsx: icons, shared hooks & small components */

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

function useDumpStore() {
  return useSyncExternalStore(DumpStore.subscribe, DumpStore.getState);
}

/* ---------- Icons (16/18px stroke set) ---------- */
function Icon({ d, size = 18, fill = false, ...rest }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24"
      fill={fill ? 'currentColor' : 'none'}
      stroke={fill ? 'none' : 'currentColor'}
      strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" {...rest}>
      {d}
    </svg>
  );
}

const Icons = {
  lock: (s) => <Icon size={s} d={<g><rect x="5" y="11" width="14" height="9" rx="2"></rect><path d="M8 11V8a4 4 0 0 1 8 0v3"></path></g>} />,
  today: (s) => <Icon size={s} d={<g><circle cx="12" cy="12" r="4"></circle><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9L17 7M7 17l-2.1 2.1"></path></g>} />,
  capture: (s) => <Icon size={s} d={<g><path d="M12 20h9"></path><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"></path></g>} />,
  tasks: (s) => <Icon size={s} d={<g><path d="M8 6h13M8 12h13M8 18h13"></path><path d="M3 6l1 1 2-2M3 12l1 1 2-2M3 18l1 1 2-2"></path></g>} />,
  done: (s) => <Icon size={s} d={<g><circle cx="12" cy="12" r="9"></circle><path d="M8.5 12.2l2.4 2.4 4.6-5"></path></g>} />,
  connect: (s) => <Icon size={s} d={<g><path d="M9 7H7a5 5 0 0 0 0 10h2"></path><path d="M15 7h2a5 5 0 0 1 0 10h-2"></path><path d="M8.5 12h7"></path></g>} />,
  gear: (s) => <Icon size={s} d={<g><circle cx="12" cy="12" r="3.2"></circle><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.87-.34 1.7 1.7 0 0 0-1.03 1.56V21a2 2 0 1 1-4 0v-.09a1.7 1.7 0 0 0-1.11-1.56 1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.7 1.7 0 0 0 .34-1.87 1.7 1.7 0 0 0-1.56-1.03H3a2 2 0 1 1 0-4h.09a1.7 1.7 0 0 0 1.56-1.11 1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.7 1.7 0 0 0 1.87.34h.08A1.7 1.7 0 0 0 10 4.09V4a2 2 0 1 1 4 0v.09a1.7 1.7 0 0 0 1.03 1.56h.08a1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.7 1.7 0 0 0-.34 1.87v.08a1.7 1.7 0 0 0 1.56 1.03H21a2 2 0 1 1 0 4h-.09a1.7 1.7 0 0 0-1.51.88Z"></path></g>} />,
  star: (s) => <Icon size={s} fill={true} d={<path d="M12 2.6l2.8 5.9 6.4.8-4.7 4.4 1.2 6.3-5.7-3.1-5.7 3.1 1.2-6.3L2.8 9.3l6.4-.8Z"></path>} />,
  starOutline: (s) => <Icon size={s} d={<path d="M12 2.6l2.8 5.9 6.4.8-4.7 4.4 1.2 6.3-5.7-3.1-5.7 3.1 1.2-6.3L2.8 9.3l6.4-.8Z"></path>} />,
  check: (s) => <Icon size={s} d={<path d="M4.5 12.5l5 5 10-11" strokeWidth="2.6"></path>} />,
  flag: (s) => <Icon size={s} fill={true} d={<path d="M5 3v18h2v-7h11l-3-4.5L18 5H7V3Z"></path>} />,
  calendar: (s) => <Icon size={s} d={<g><rect x="3" y="5" width="18" height="16" rx="2"></rect><path d="M3 10h18M8 3v4M16 3v4"></path></g>} />,
  mail: (s) => <Icon size={s} d={<g><rect x="3" y="5" width="18" height="14" rx="2"></rect><path d="M3 7l9 6 9-6"></path></g>} />,
  chat: (s) => <Icon size={s} d={<path d="M21 12a8 8 0 0 1-8 8c-1.4 0-2.8-.3-4-1l-5 1.3L5.3 16A8 8 0 1 1 21 12Z"></path>} />,
  slack: (s) => <Icon size={s} d={<g><path d="M9 4a2 2 0 1 1 4 0v5a2 2 0 1 1-4 0Z"></path><path d="M20 9a2 2 0 1 1 0 4h-5a2 2 0 1 1 0-4Z"></path><path d="M15 20a2 2 0 1 1-4 0v-5a2 2 0 1 1 4 0Z"></path><path d="M4 15a2 2 0 1 1 0-4h5a2 2 0 1 1 0 4Z"></path></g>} />,
  file: (s) => <Icon size={s} d={<g><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"></path><path d="M14 2v6h6M9 13h6M9 17h6"></path></g>} />,
  sun: (s) => <Icon size={s} d={<g><circle cx="12" cy="12" r="4"></circle><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9L17 7M7 17l-2.1 2.1"></path></g>} />,
  sparkle: (s) => <Icon size={s} d={<path d="M12 3l1.9 5.6L19.5 10l-5.6 1.9L12 17.5l-1.9-5.6L4.5 10l5.6-1.4Z"></path>} />,
  inbox: (s) => <Icon size={s} d={<g><path d="M22 12h-6l-2 3h-4l-2-3H2"></path><path d="M5.45 5.1L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.9A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.1Z"></path></g>} />,
  bolt: (s) => <Icon size={s} fill={true} d={<path d="M13 2L4.5 13.5h6L11 22l8.5-11.5h-6L13 2Z"></path>} />,
  pause: (s) => <Icon size={s} d={<g><circle cx="12" cy="12" r="9"></circle><path d="M10 9v6M14 9v6"></path></g>} />,
  hourglass: (s) => <Icon size={s} d={<g><path d="M7 3h10M7 21h10M8 3c0 7 8 7 8 11M16 3c0 7-8 7-8 11M8 21c0-4 2.7-5 4-7 1.3 2 4 3 4 7"></path></g>} />,
  trash: (s) => <Icon size={s} d={<g><path d="M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6.5 7l1 13a1 1 0 0 0 1 .9h7a1 1 0 0 0 1-.9l1-13"></path><path d="M10 11v6M14 11v6"></path></g>} />,
  sweep: (s) => <Icon size={s} d={<g><path d="M20 4l-8.5 8.5"></path><path d="M11.5 12.5l-7.2 3.4a1 1 0 0 0-.2 1.7l2.8 2.8a1 1 0 0 0 1.7-.2l3.4-7.2Z"></path><path d="M7 15.5l1.6 1.6M9.6 14l1.5 1.5"></path></g>} />,
  cart: (s) => <Icon size={s} d={<g><path d="M3 4h2l2.4 12.3a1 1 0 0 0 1 .8h8.7a1 1 0 0 0 1-.78L21 8H6"></path><circle cx="9.5" cy="20" r="1.2" fill="currentColor" stroke="none"></circle><circle cx="17.5" cy="20" r="1.2" fill="currentColor" stroke="none"></circle></g>} />,
  x: (s) => <Icon size={s} d={<path d="M6 6l12 12M18 6L6 18"></path>} />,
  more: (s) => <Icon size={s} d={<g><circle cx="5" cy="12" r="1.7" fill="currentColor" stroke="none"></circle><circle cx="12" cy="12" r="1.7" fill="currentColor" stroke="none"></circle><circle cx="19" cy="12" r="1.7" fill="currentColor" stroke="none"></circle></g>} />,
  timer: (s) => <Icon size={s} d={<g><circle cx="12" cy="13" r="8"></circle><path d="M12 13V9M12 13l3 2M9 2h6M18 5l1.5 1.5"></path></g>} />,
  chevL: (s) => <Icon size={s} d={<path d="M15 6l-6 6 6 6"></path>} />,
  chevR: (s) => <Icon size={s} d={<path d="M9 6l6 6-6 6"></path>} />,
  play: (s) => <Icon size={s} fill={true} d={<path d="M7 4.5v15l13-7.5Z"></path>} />,
  sun: (s) => <Icon size={s} d={<g><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4"></path></g>} />,
  moon: (s) => <Icon size={s} d={<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z"></path>} />,
  repeat: (s) => <Icon size={s} d={<g><path d="M17 2l3 3-3 3"></path><path d="M3 12V11a4 4 0 0 1 4-4h13"></path><path d="M7 22l-3-3 3-3"></path><path d="M21 12v1a4 4 0 0 1-4 4H4"></path></g>} />,
  tag: (s) => <Icon size={s} d={<g><path d="M11.6 3H5a2 2 0 0 0-2 2v6.6a2 2 0 0 0 .6 1.4l7.4 7.4a2 2 0 0 0 2.8 0l6.6-6.6a2 2 0 0 0 0-2.8L13 3.6A2 2 0 0 0 11.6 3Z"></path><circle cx="7.5" cy="7.5" r="1.1" fill="currentColor" stroke="none"></circle></g>} />,
  note: (s) => <Icon size={s} d={<g><path d="M4 5h16a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H9l-4 4v-4H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Z"></path><path d="M7.5 9.5h9M7.5 12.5h5.5"></path></g>} />,
  filter: (s) => <Icon size={s} d={<path d="M3 5h18l-7 8.5V20l-4-2v-4.5Z"></path>} />,
  paperclip: (s) => <Icon size={s} d={<path d="M21.4 11.05l-8.49 8.49a5 5 0 0 1-7.07-7.07l8.49-8.49a3 3 0 0 1 4.24 4.24l-8.49 8.49a1 1 0 0 1-1.41-1.41l7.78-7.78"></path>} />,
  mic: (s) => <Icon size={s} d={<g><rect x="9" y="3" width="6" height="11" rx="3"></rect><path d="M5 11a7 7 0 0 0 14 0M12 18v3M8.5 21h7"></path></g>} />,
  plus: (s) => <Icon size={s} d={<path d="M12 5v14M5 12h14"></path>} />,
  swap: (s) => <Icon size={s} d={<g><path d="M3 8h13l-3-3M21 16H8l3 3"></path></g>} />
};

/* ---------- Category pill ---------- */
function CatPill({ name }) {
  if (!name) return null;
  return <span className="cat-tag">{Icons.tag(13)} {name}</span>;
}

/* ---------- Banner ---------- */
function Banner({ icon, children, onClose }) {
  return (
    <div className="banner">
      {icon || Icons.sparkle(17)}
      <span style={{ flex: 1 }}>{children}</span>
      {onClose ? <button className="banner-close" onClick={onClose} aria-label="Dismiss">×</button> : null}
    </div>
  );
}

/* ---------- Empty state ---------- */
function EmptyState({ icon, title, children }) {
  return (
    <div className="empty">
      <div className="empty-art">{icon}</div>
      <p className="empty-title">{title}</p>
      <p>{children}</p>
    </div>
  );
}

/* ---------- Toast stack ---------- */
function ToastStack() {
  const { toasts } = useDumpStore();
  if (!toasts.length) return null;
  return (
    <div className="toast-stack">
      {toasts.map((t) => (
        <div className="toast" key={t.id}>
          <span>{t.msg}</span>
          {t.retry ? <button className="linklike" onClick={t.retry}>Retry</button> : null}
          {t.action ? <button className="linklike" onClick={t.action.fn}>{t.action.label}</button> : null}
        </div>
      ))}
    </div>
  );
}

/* ---------- Tooltip + info icon (inline GTD teaching) ---------- */
function Tooltip({ text, children }) {
  const [open, setOpen] = useState(false);
  // Desktop: CSS hover. Mobile: tap the trigger to toggle.
  return (
    <span className="tooltip-wrap"
      onClick={(e) => { e.stopPropagation(); setOpen((o) => !o); }}
      onMouseLeave={() => setOpen(false)}>
      {children}
      <span className={'tooltip-text' + (open ? ' tooltip-open' : '')} role="tooltip">{text}</span>
    </span>
  );
}

// A small "?" badge that reveals a Tooltip. Always a <span> — these sit inside
// <button> filter pills, and a button-in-button would be invalid HTML.
function InfoIcon({ text }) {
  return (
    <Tooltip text={text}>
      <span className="info-icon" aria-label="More info" tabIndex={0}>?</span>
    </Tooltip>
  );
}

/* ---------- One-time contextual hint (dismissed forever) ---------- */
function ContextHint({ id, icon, children }) {
  const { seenHints } = useDumpStore();
  if ((seenHints || []).includes(id)) return null;
  return (
    <div className="context-hint">
      {icon || Icons.sparkle(16)}
      <span className="context-hint-body">{children}</span>
      <button className="context-hint-dismiss" onClick={(e) => { e.stopPropagation(); DumpAPI.dismissHint(id); }} aria-label="Dismiss">×</button>
    </div>
  );
}

Object.assign(window, { useDumpStore, Icons, CatPill, Banner, EmptyState, ToastStack, Tooltip, InfoIcon, ContextHint });
