// ── Org (company) Slack connector — live UI ─────────────────────────────────
// Rendered by ConnectorsHub (org mode) when a live backend is configured. The
// first REAL connector on the org console: everything else on the page is
// still the demo catalog. Connect → OAuth popup → the backend's callback
// auto-kicks a sync that parks at awaiting_selection → pick channels → ingest.
// Ingested conversations land in the org's memory (sources/chunks), where the
// knowledge agent's document search already reads them.

const { useState: useOsState, useMemo: useOsMemo } = React;

function OrgSlackChannelPicker({ items, onIngest, busy }) {
  // Only 'discovered' (new/changed) channels are selectable. Already-ingested
  // ('unchanged') ones render as checked+disabled "In memory"; skipped ones
  // (e.g. over the message cap) render unchecked+disabled with a Skipped chip.
  const selectable = useOsMemo(() => items.filter(i => i.status === 'discovered'), [items]);
  const [picked, setPicked] = useOsState(() => new Set(selectable.map(i => i.item_id)));
  const toggle = (id) => setPicked(prev => {
    const next = new Set(prev);
    if (next.has(id)) next.delete(id); else next.add(id);
    return next;
  });
  const allPicked = picked.size === selectable.length && selectable.length > 0;
  const toggleAll = () => setPicked(allPicked ? new Set() : new Set(selectable.map(i => i.item_id)));

  const msgCount = (i) => i.message_count != null ? i.message_count : i.size_bytes;

  return (
    <div style={{ marginTop: 12 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
        <div className="t-h3">Choose channels to ingest</div>
        {selectable.length > 0 && (
          <button className="btn sm ghost" onClick={toggleAll} disabled={busy}>
            {allPicked ? 'Clear all' : 'Select all'}
          </button>
        )}
      </div>
      <div style={{ maxHeight: 280, overflowY: 'auto', border: '1px solid var(--border-subtle)', borderRadius: 8 }}>
        {items.length === 0 && (
          <div className="text-secondary t-small" style={{ padding: 14 }}>
            No conversations found in this workspace.
          </div>
        )}
        {items.map(i => {
          const selectableRow = i.status === 'discovered';
          const inMemory = i.status === 'unchanged';
          const skipped = !selectableRow && !inMemory;
          const count = msgCount(i);
          return (
            <label key={i.item_id} style={{
              display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px',
              borderBottom: '1px solid var(--border-subtle)', cursor: selectableRow ? 'pointer' : 'default',
              opacity: selectableRow ? 1 : 0.6,
            }}>
              <input type="checkbox" disabled={busy || !selectableRow}
                checked={inMemory ? true : picked.has(i.item_id)}
                onChange={() => toggle(i.item_id)}/>
              <span style={{ flex: 1, fontSize: 13, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {i.item_name || i.item_id}
              </span>
              {count != null && (
                <span className="mono-sm mono text-tertiary">{count} msgs</span>
              )}
              {inMemory && <span className="chip" style={{ fontSize: 10, height: 20 }}>In memory</span>}
              {skipped && (
                <span className="chip" style={{ fontSize: 10, height: 20 }}
                  title={i.skip_reason || i.status}>
                  {i.status === 'skipped_too_large' ? 'Too large' : 'Skipped'}
                </span>
              )}
            </label>
          );
        })}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 10 }}>
        {selectable.length === 0 ? (
          // Nothing selectable (everything already in memory or skipped): a
          // disabled "Ingest 0 channels" reads as broken, so say what's up —
          // the card's Sync now / Disconnect buttons are the way out.
          items.length > 0 && (
            <span className="text-secondary t-small">
              {items.some(i => i.status !== 'unchanged')
                ? 'No channels can be ingested — everything here is already in memory or was skipped.'
                : 'No new channels to ingest — everything here is already in memory.'}
            </span>
          )
        ) : (
          <>
            <button className="btn accent" disabled={busy || picked.size === 0}
              onClick={() => onIngest({ itemIds: Array.from(picked) })}>
              Ingest {picked.size} channel{picked.size === 1 ? '' : 's'}
            </button>
            <span className="mono-sm mono text-tertiary">
              Messages become searchable org knowledge.
            </span>
          </>
        )}
      </div>
    </div>
  );
}

function OrgSlackSyncProgress({ job }) {
  const payload = job.payload || {};
  const counts = payload.counts || {};
  const total = counts.total || 0;
  const done = (counts.done || 0) + (counts.failed || 0);
  const pct = typeof job.progress === 'number' ? Math.round(job.progress * 100)
    : (total > 0 ? Math.round((done / total) * 100) : 0);
  const phase = payload.phase || job.status;
  return (
    <div style={{ marginTop: 12 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
        <span className="mono-sm mono text-secondary">
          {phase === 'discovering' || job.status === 'pending' ? 'Scanning workspace…'
            : `Ingesting conversations… ${total ? `${done}/${total}` : ''}`}
        </span>
        <span className="mono-sm mono text-tertiary">{pct}%</span>
      </div>
      <Progress value={Math.max(2, pct)} height={4}/>
    </div>
  );
}

function OrgSlackCard() {
  const { phase, syncJob, items, error, connect, cancelConnect, syncNow, ingest, disconnect } = useOrgSlack();
  const [busy, setBusy] = useOsState(false);
  const run = (fn) => async (...args) => {
    setBusy(true);
    try { await fn(...args); } finally { setBusy(false); }
  };

  const jobActive = syncJob && osJobActive(syncJob);
  const awaiting = phase === 'connected' && syncJob && syncJob.status === 'awaiting_selection';
  const failed = syncJob && syncJob.status === 'failed';
  const cancelled = syncJob && syncJob.status === 'cancelled';
  const donePayload = (syncJob && syncJob.status === 'done' && (syncJob.payload || {})) || null;
  const doneCounts = (donePayload && donePayload.counts) || null;
  // A sync that found nothing new/changed (the backend completes it as done
  // instead of parking an empty picker). "Skipped" discoveries (e.g. over the
  // message cap) also end here — the counts tell the two apart.
  const nothingNew = !!(donePayload && donePayload.nothing_new);
  const nothingNewSkipped = nothingNew && doneCounts
    ? (doneCounts.skipped_too_large || 0) + (doneCounts.skipped_too_small || 0)
      + (doneCounts.skipped_unsupported || 0)
    : 0;
  // An awaiting_selection picker with nothing selectable (everything unchanged
  // or skipped) is a dead end — bring back the Sync now / Disconnect exits.
  const emptyAwaiting = awaiting && !!items && !items.some(i => i.status === 'discovered');

  const statusLine =
    phase === 'loading' ? 'Checking status…'
    : phase === 'connecting' ? 'Complete the authorization in the Slack window…'
    : phase === 'disconnected' ? 'Not connected'
    : jobActive ? 'Syncing'
    : emptyAwaiting ? 'No new channels to ingest'
    : awaiting ? 'Choose channels to ingest'
    : failed ? 'Last sync failed'
    : cancelled ? 'Last sync cancelled'
    : nothingNew ? (nothingNewSkipped > 0
        ? `No ingestable conversations — ${nothingNewSkipped} skipped`
        : 'No new conversations — everything already in memory')
    : doneCounts ? `${doneCounts.done || 0} conversations in memory`
    : 'Connected';

  return (
    <div id="org-slack-connector" className="card" style={{ padding: '18px 22px', marginTop: 24 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
        <VendorLogo vendor="chatroom" size={36} radius={9}/>
        <div style={{ flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span className="t-h3">Slack</span>
            <span className="chip accent" style={{ fontSize: 10, height: 20 }}>Live</span>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 3 }}>
            <StatusDot size={6} kind={
              phase === 'connected' ? (jobActive ? 'syncing' : failed ? 'error' : cancelled ? 'paused' : 'live')
              : phase === 'connecting' ? 'syncing' : 'paused'
            }/>
            <span className="mono-sm mono text-secondary">{statusLine}</span>
          </div>
        </div>
        {phase === 'disconnected' && (
          <button className="btn accent" disabled={busy} onClick={run(connect)}>
            Connect Slack
          </button>
        )}
        {phase === 'connecting' && (
          <button className="btn ghost" onClick={cancelConnect}>Cancel</button>
        )}
        {phase === 'connected' && !jobActive && (!awaiting || emptyAwaiting) && (
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="btn" disabled={busy} onClick={run(syncNow)}>Sync now</button>
            <button className="btn ghost" disabled={busy} onClick={run(disconnect)}>Disconnect</button>
          </div>
        )}
      </div>

      {phase === 'disconnected' && (
        <div className="text-secondary t-small" style={{ marginTop: 10 }}>
          Authorize with the Slack account whose conversations should become org
          knowledge — channels, group and direct messages it can read are
          offered for ingestion, and new messages keep flowing in.
        </div>
      )}

      {error && (
        <div className="t-small" style={{ marginTop: 10, color: 'var(--error)' }}>{error}</div>
      )}

      {jobActive && <OrgSlackSyncProgress job={syncJob}/>}
      {awaiting && items && (
        <OrgSlackChannelPicker items={items} busy={busy} onIngest={run(ingest)}/>
      )}
      {failed && (
        <div style={{ marginTop: 10 }}>
          <button className="btn sm" disabled={busy} onClick={run(syncNow)}>Retry sync</button>
        </div>
      )}
    </div>
  );
}

// Section wrapper ConnectorsHub renders: only exists on a live backend (mock
// demo mode keeps the page unchanged).
function OrgSlackSection() {
  if (!osLive()) return null;
  return <OrgSlackCard/>;
}

Object.assign(window, { OrgSlackSection });
