// ============================================================================
// IMS — Shared primitives (components shared across all roles)
// ============================================================================

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

// ---------------- App context ----------------

const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);

// ---------------- Icons (inline SVG) ----------------

const Ic = {
  search: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><circle cx="9" cy="9" r="6"/><path d="M14 14l4 4"/></svg>,
  plus: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><path d="M10 4v12M4 10h12"/></svg>,
  x: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><path d="M5 5l10 10M15 5L5 15"/></svg>,
  check: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" width="14" height="14" {...p}><path d="M4 10.5l4 4 8-9"/></svg>,
  arrowRight: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><path d="M4 10h12M11 5l5 5-5 5"/></svg>,
  arrowLeft: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><path d="M16 10H4M9 5l-5 5 5 5"/></svg>,
  ext: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="12" height="12" {...p}><path d="M8 4H4v12h12v-4M12 4h4v4M9 11l7-7"/></svg>,
  upload: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><path d="M4 13v2a1 1 0 001 1h10a1 1 0 001-1v-2M10 4v9M6 8l4-4 4 4"/></svg>,
  mic: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><rect x="8" y="3" width="4" height="9" rx="2"/><path d="M5 10a5 5 0 0010 0M10 15v3M7 18h6"/></svg>,
  info: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="13" height="13" {...p}><circle cx="10" cy="10" r="7"/><path d="M10 9v4M10 6.5v.01"/></svg>,
  warn: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="13" height="13" {...p}><path d="M10 3L2 17h16L10 3z"/><path d="M10 9v3M10 14.5v.01"/></svg>,
  filter: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="12" height="12" {...p}><path d="M3 5h14M6 10h8M9 15h2"/></svg>,
  caret: (p) => <svg viewBox="0 0 10 10" fill="currentColor" width="8" height="8" {...p}><path d="M5 7L1 3h8z"/></svg>,
  dots: (p) => <svg viewBox="0 0 20 20" fill="currentColor" width="14" height="14" {...p}><circle cx="4" cy="10" r="1.3"/><circle cx="10" cy="10" r="1.3"/><circle cx="16" cy="10" r="1.3"/></svg>,
  bell: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><path d="M5 9a5 5 0 0110 0v4l1 2H4l1-2V9zM8 17a2 2 0 004 0"/></svg>,
  linear: (p) => <svg viewBox="0 0 20 20" fill="currentColor" width="12" height="12" {...p}><path d="M2 13.2l4.8 4.8c-2.6-.6-4.6-2.6-4.8-4.8zM2.3 9.5l8.2 8.2c-.7.2-1.5.3-2.3.3L2 11.8a8 8 0 01.3-2.3zM3.3 7.2L12.8 16.7a7 7 0 002-1L4.3 5.2a7 7 0 00-1 2zM6 4a7 7 0 00-1 .9L15.1 15a7 7 0 00.9-1L6 4zM17 10a7 7 0 10-7 7 7 7 0 007-7z"/></svg>,
  copy: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="13" height="13" {...p}><rect x="6" y="6" width="10" height="10" rx="1"/><path d="M4 14V5a1 1 0 011-1h9"/></svg>,
  gear: (p) => <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.6" width="14" height="14" {...p}><circle cx="10" cy="10" r="2.8"/><path d="M10 2v2M10 16v2M2 10h2M16 10h2M4.2 4.2l1.4 1.4M14.4 14.4l1.4 1.4M4.2 15.8l1.4-1.4M14.4 5.6l1.4-1.4"/></svg>,
};

// ---------------- Helper components ----------------

function Chip({ tone = "neutral", square, mono, outline, xs, children }) {
  const cls = ["chip", tone, square && "square", mono && "mono", outline && "outline", xs && "xs"].filter(Boolean).join(" ");
  return <span className={cls}>{children}</span>;
}

function InfCell({ name, handle, avatarTone }) {
  const initials = name.split(" ").map(n => n[0]).slice(0, 2).join("");
  return (
    <div className="inf-cell">
      <div className="avatar" style={avatarTone ? { background: avatarTone } : null}>{initials}</div>
      <div className="name-block">
        <div className="name">{name}</div>
        <div className="meta">{handle}</div>
      </div>
    </div>
  );
}

function StatusChip({ status }) {
  const map = {
    "Not Contacted": "neutral",
    "Contacted": "info",
    "Accepted": "ok",
    "Interested": "accent-soft",
    "Rejected": "bad",
    "Paid": "ok",
    "Unpaid": "warn",
    "Pending": "neutral",
    "In Progress": "info",
    "Complete": "ok",
    "Locked": "neutral",
    "Open": "warn",
    "Awaiting Admin": "warn",
    "Resolved": "ok",
    "Provided": "ok",
    "Not Interested": "bad",
    "Not Responding": "neutral",
    "Approved": "ok",
    "Denied": "bad",
    "Skipped": "neutral",
    "Dropped": "bad",
  };
  return <Chip tone={map[status] || "neutral"}>{status}</Chip>;
}

function BundleIdChip({ id }) {
  const isLead = id?.startsWith("LB");
  return (
    <span className={`chip mono square ${isLead ? "accent-soft" : "neutral"}`} title={isLead ? "Lead Bundle" : "Outreach Bundle"}>
      {id}
    </span>
  );
}

function NotesCell({ note, count = 3 }) {
  const { role } = useApp();
  if (role !== "admin") return <span className="text-mute">—</span>;
  if (!note) return <span className="text-mute text-xs">No notes</span>;
  return (
    <td className="notes-cell" onClick={(e) => e.stopPropagation()}>
      <div className="note-preview">{note}</div>
      <div className="note-tooltip">
        <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.1em", textTransform: "uppercase", opacity: 0.6, marginBottom: 6 }}>
          Latest · {count} total
        </div>
        {note}
      </div>
    </td>
  );
}

// ---------------- Modal ----------------

function Modal({ open, onClose, title, sub, size, children, foot }) {
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => e.key === "Escape" && onClose?.();
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open, onClose]);

  if (!open) return null;
  const sizeCls = size === "wide" ? "wide" : size === "xwide" ? "xwide" : "";
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className={`modal ${sizeCls}`} onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            {sub && <div className="sub">{sub}</div>}
            <div className="title">{title}</div>
          </div>
          <button className="btn ghost sm" onClick={onClose} aria-label="Close"><Ic.x /></button>
        </div>
        <div className="modal-body">{children}</div>
        {foot && <div className="modal-foot">{foot}</div>}
      </div>
    </div>
  );
}

// ---------------- Toasts ----------------

function Toast({ items }) {
  return (
    <div className="toast-stack">
      {items.map((t) => (
        <div key={t.id} className="toast">
          {t.tick !== false && <span className="tick-mini" />}
          <span>{t.msg}</span>
        </div>
      ))}
    </div>
  );
}

// ---------------- Column filter (dropdown) ----------------

function useColumnFilter(rows, columns) {
  // columns: [{ key, label, accessor }]
  const [filters, setFilters] = useState({}); // { key: value }
  const [openFor, setOpenFor] = useState(null);

  const filtered = useMemo(() => {
    if (Object.keys(filters).length === 0) return rows;
    return rows.filter(r => Object.entries(filters).every(([k, v]) => {
      const col = columns.find(c => c.key === k);
      const got = col.accessor(r);
      return got === v;
    }));
  }, [rows, filters, columns]);

  // Cache distinct values per column — only recomputed when rows change
  const distinctsCache = useMemo(() => {
    const cache = {};
    columns.forEach(col => {
      const s = new Set(rows.map(col.accessor).filter(Boolean));
      cache[col.key] = [...s].sort();
    });
    return cache;
  }, [rows, columns]);

  const distinctFor = (key) => distinctsCache[key] || [];

  const set = (k, v) => setFilters({ ...filters, [k]: v });
  const clear = (k) => { const next = { ...filters }; delete next[k]; setFilters(next); };

  return { filtered, filters, set, clear, openFor, setOpenFor, distinctFor };
}

function FilterHeader({ col, filters, openFor, setOpenFor, distincts, onPick, onClear }) {
  const active = filters[col.key] != null;
  const isOpen = openFor === col.key;
  const thRef  = useRef(null);
  const dropRef = useRef(null);
  const [search, setSearch] = useState("");
  const [alignRight, setAlignRight] = useState(false);

  // Close on outside click
  useEffect(() => {
    if (!isOpen) return;
    const handler = (e) => {
      if (thRef.current && !thRef.current.contains(e.target)) setOpenFor(null);
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [isOpen, setOpenFor]);

  // Flip to right-align if dropdown would overflow viewport
  useEffect(() => {
    if (isOpen && dropRef.current) {
      const rect = dropRef.current.getBoundingClientRect();
      setAlignRight(rect.right > window.innerWidth - 12);
    }
    if (!isOpen) { setSearch(""); setAlignRight(false); }
  }, [isOpen]);

  const visibleOptions = useMemo(() => {
    const q = search.trim().toLowerCase();
    return q ? distincts.filter(v => v.toLowerCase().includes(q)) : distincts;
  }, [distincts, search]);

  return (
    <th ref={thRef} className={active ? "filtered" : ""} style={{ position: "relative" }}
      onClick={(e) => { e.stopPropagation(); setOpenFor(isOpen ? null : col.key); }}>
      <span style={{ display: "inline-flex", alignItems: "center", gap: 6, whiteSpace: "nowrap" }}>
        {col.label}
        <Ic.caret className="caret" />
      </span>
      {isOpen && (
        <div ref={dropRef} onClick={(e) => e.stopPropagation()} style={{
          position: "absolute", top: "calc(100% + 4px)",
          [alignRight ? "right" : "left"]: 0,
          zIndex: 40,
          background: "var(--bg)", border: "1px solid var(--line-2)",
          boxShadow: "var(--shadow-md)", borderRadius: 4,
          minWidth: 210, width: "max-content", maxWidth: 280,
        }}>
          {/* Search input */}
          <div style={{ padding: "8px 10px", borderBottom: "1px solid var(--line)" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 6, background: "var(--bg-2)", borderRadius: 3, padding: "5px 8px", border: "1px solid var(--line)" }}>
              <Ic.search style={{ color: "var(--ink-3)", flexShrink: 0 }} />
              <input
                autoFocus
                placeholder={`Search ${col.label}…`}
                value={search}
                onChange={e => setSearch(e.target.value)}
                onClick={e => e.stopPropagation()}
                style={{
                  border: "none", outline: "none", background: "transparent",
                  fontSize: 12.5, fontFamily: "inherit", color: "var(--ink)",
                  width: "100%", minWidth: 0,
                }}
              />
              {search && (
                <button onMouseDown={(e) => { e.preventDefault(); setSearch(""); }}
                  style={{ border: "none", background: "none", cursor: "pointer", color: "var(--ink-3)", padding: 0, display: "flex", alignItems: "center" }}>
                  <Ic.x width="11" height="11" />
                </button>
              )}
            </div>
          </div>

          {/* Options list — scrollable */}
          <div style={{ maxHeight: 220, overflowY: "auto" }}>
            {visibleOptions.length === 0 ? (
              <div style={{ padding: "12px 10px", fontSize: 12, color: "var(--ink-3)", textAlign: "center" }}>
                No matches for "{search}"
              </div>
            ) : visibleOptions.map(v => (
              <button key={v} onMouseDown={(e) => { e.preventDefault(); onPick(v); setOpenFor(null); }} style={{
                display: "flex", alignItems: "center", gap: 8,
                width: "100%", textAlign: "left",
                padding: "7px 10px", fontSize: 12.5, borderRadius: 0,
                background: filters[col.key] === v ? "var(--bg-2)" : "transparent",
                border: "none", cursor: "pointer", fontFamily: "inherit",
                color: "var(--ink)",
              }}>
                {filters[col.key] === v && <span className="tick" style={{ flexShrink: 0 }} />}
                {v}
              </button>
            ))}
          </div>

          {/* Clear filter */}
          {active && (
            <>
              <div style={{ height: 1, background: "var(--line)" }} />
              <button onMouseDown={(e) => { e.preventDefault(); onClear(); setOpenFor(null); }} style={{
                display: "block", width: "100%", textAlign: "left",
                padding: "7px 10px", fontSize: 12, color: "var(--bad)",
                fontFamily: "inherit", border: "none", cursor: "pointer",
                background: "transparent",
              }}>
                Clear filter
              </button>
            </>
          )}
        </div>
      )}
    </th>
  );
}

function ActiveFilterChips({ filters, columns, onClear }) {
  const entries = Object.entries(filters);
  if (!entries.length) return null;
  return (
    <div style={{ display: "flex", gap: 6, alignItems: "center", padding: "10px 0" }}>
      <span className="text-mono text-xs text-mute" style={{ textTransform: "uppercase", letterSpacing: "0.1em" }}>Filters</span>
      {entries.map(([k, v]) => {
        const col = columns.find(c => c.key === k);
        return (
          <span key={k} className="filter-chip">
            {col?.label}: {v}
            <button className="x" onClick={() => onClear(k)}><Ic.x /></button>
          </span>
        );
      })}
    </div>
  );
}

// ============================================================================
// Settings
// ============================================================================

const SETTINGS_KEY = "ims:settings:v1";
const DEFAULT_SETTINGS = { bundleSize: 2, snapshotDay: "Friday", snapshotTime: "14:00", minPaymentThreshold: 50 };

function loadSettings() {
  try { const r = localStorage.getItem(SETTINGS_KEY); return r ? JSON.parse(r) : null; } catch { return null; }
}
function saveSettings(s) { localStorage.setItem(SETTINGS_KEY, JSON.stringify(s)); }

function useSettings() {
  const [settings, setS] = useState(() => ({ ...DEFAULT_SETTINGS, ...(loadSettings() || {}) }));
  const update = (u) => setS(s => { const n = { ...s, ...u }; saveSettings(n); return n; });
  return [settings, update];
}

window.useSettings = useSettings;

function SettingsPage() {
  const { notify } = useApp();
  const [settings, updateSettings] = useSettings();
  const [local, setLocal] = useState({ ...settings });
  const dirty = JSON.stringify(local) !== JSON.stringify(settings);

  const save = () => { updateSettings(local); notify("Settings saved.", "success"); };

  const SectionLabel = ({ children }) => (
    <div className="text-mono text-xs text-mute" style={{ textTransform: "uppercase", letterSpacing: "0.12em", marginBottom: 16 }}>{children}</div>
  );
  const Field = ({ label, hint, children }) => (
    <div className="field" style={{ marginBottom: 20 }}>
      <label style={{ display: "block", fontWeight: 600, fontSize: 13, marginBottom: 4 }}>{label}</label>
      {hint && <div className="text-xs text-mute" style={{ marginBottom: 8 }}>{hint}</div>}
      {children}
    </div>
  );

  return (
    <div className="page">
      <div className="page-head">
        <div><h1>Settings</h1><div className="subtitle">System-wide configuration — Admin only.</div></div>
      </div>
      <div style={{ maxWidth: 560 }}>

        <div className="card" style={{ marginBottom: 16 }}>
          <SectionLabel>Bundle Configuration</SectionLabel>
          <Field label="Max Bundle Size" hint="Maximum number of influencers that can be assigned in a single bundle.">
            <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
              <input className="input" type="number" min={1} max={100} style={{ width: 100 }}
                value={local.bundleSize}
                onChange={e => setLocal(s => ({ ...s, bundleSize: Math.max(1, Math.min(100, +e.target.value || 1)) }))} />
              <span className="text-xs text-mute">influencers per bundle</span>
            </div>
          </Field>
        </div>

        <div className="card" style={{ marginBottom: 16 }}>
          <SectionLabel>Payment Snapshot</SectionLabel>
          <Field label="Snapshot Day" hint="Day of week the weekly payment snapshot runs automatically.">
            <select className="select" style={{ width: 180 }} value={local.snapshotDay}
              onChange={e => setLocal(s => ({ ...s, snapshotDay: e.target.value }))}>
              {["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"].map(d => <option key={d}>{d}</option>)}
            </select>
          </Field>
          <Field label="Snapshot Time (UK)" hint="Time in UK timezone when the snapshot fires.">
            <input className="input" type="time" style={{ width: 140 }} value={local.snapshotTime}
              onChange={e => setLocal(s => ({ ...s, snapshotTime: e.target.value }))} />
          </Field>
          <Field label="Minimum Payment Threshold (USD)" hint="Payments below this roll over to the next cycle rather than being sent to finance.">
            <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
              <input className="input" type="number" min={0} style={{ width: 120 }} value={local.minPaymentThreshold}
                onChange={e => setLocal(s => ({ ...s, minPaymentThreshold: Math.max(0, +e.target.value || 0) }))} />
              <span className="text-xs text-mute">USD minimum</span>
            </div>
          </Field>
        </div>

        <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
          {dirty && <button className="btn" onClick={() => setLocal({ ...settings })}>Discard</button>}
          <button className="btn primary" disabled={!dirty} onClick={save}>Save Settings</button>
        </div>
      </div>
    </div>
  );
}

// ---------------- Exports ----------------

Object.assign(window, {
  useState, useMemo, useEffect, useRef, useCallback,
  AppCtx, useApp,
  Ic, Chip, InfCell, StatusChip, BundleIdChip, NotesCell,
  Modal, Toast,
  useColumnFilter, FilterHeader, ActiveFilterChips,
  SettingsPage, useSettings,
});
