// ── Org (company) Telegram connector — live UI ──────────────────────────────
// Rendered by ConnectorsHub (org mode) when a live backend is configured.
// Telegram has no OAuth popup: Connect opens an inline phone-code login form
// (phone → code → optional 2FA password) right in the card. On success the
// hook kicks the first sync, which parks at awaiting_selection → pick chats →
// ingest. Ingested conversations land in the org's memory (sources/chunks),
// where the knowledge agent's document search already reads them.

const { useState: useOtState, useMemo: useOtMemo } = React;

function OrgTelegramChatPicker({ items, onIngest, busy }) {
  // Only 'discovered' (new/changed) chats 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 = useOtMemo(() => items.filter(i => i.status === 'discovered'), [items]);
  // Chats default UNPICKED (unlike Slack's channels): a personal Telegram is
  // mostly private 1:1 chats, and "everything preselected" makes it far too
  // easy to pour those into shared org memory with one click.
  const [picked, setPicked] = useOtState(() => new Set());
  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 chats 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 chats found in this Telegram account.
          </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 chats" 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 chats can be ingested — everything here is already in memory or was skipped.'
                : 'No new chats 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} chat{picked.size === 1 ? '' : 's'}
            </button>
            <span className="mono-sm mono text-tertiary">
              Selected chats become searchable org knowledge.
            </span>
          </>
        )}
      </div>
    </div>
  );
}

function OrgTelegramSyncProgress({ 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 chats…'
            : `Ingesting chats… ${total ? `${done}/${total}` : ''}`}
        </span>
        <span className="mono-sm mono text-tertiary">{pct}%</span>
      </div>
      <Progress value={Math.max(2, pct)} height={4}/>
    </div>
  );
}

// Inline phone-code login: one input per step (phone → code → 2FA password),
// submitted on Enter or the button. `busy` (the card-level runner) covers the
// in-flight request, so a double submit can't fire two send-codes.
function OrgTelegramLoginForm({ step, busy, onSendCode, onVerifyCode, onVerifyPassword, onCancel }) {
  const [value, setValue] = useOtState('');
  // The input is reused across steps — clear it when the step advances so the
  // phone number never lingers in the code field (nor the code in the
  // password field).
  const [lastStep, setLastStep] = useOtState(step);
  if (lastStep !== step) { setLastStep(step); setValue(''); }

  const conf = step === 'phone' ? {
    label: 'Phone number',
    placeholder: '+1 555 0100',
    type: 'tel',
    action: 'Send code',
    submit: onSendCode,
    hint: 'The phone number of the Telegram account to connect, in international format. Telegram sends a login code to that account.',
  } : step === 'code' ? {
    label: 'Login code',
    placeholder: '12345',
    type: 'text',
    action: 'Verify code',
    submit: onVerifyCode,
    hint: 'Enter the code Telegram just delivered to that account (in the Telegram app, or by SMS).',
  } : {
    label: 'Two-step password',
    placeholder: 'Telegram cloud password',
    type: 'password',
    action: 'Verify password',
    submit: onVerifyPassword,
    hint: 'This account has two-step verification — enter its Telegram cloud password to finish.',
  };

  const canSubmit = !busy && value.trim().length > 0;
  const submit = () => { if (canSubmit) conf.submit(value.trim()); };

  return (
    <div style={{ marginTop: 12, maxWidth: 460 }}>
      <div className="t-small" style={{ fontWeight: 600, marginBottom: 6 }}>{conf.label}</div>
      <div style={{ display: 'flex', gap: 8 }}>
        <input className="input" style={{ flex: 1 }} type={conf.type} autoFocus
          placeholder={conf.placeholder} value={value} disabled={busy}
          onChange={e => setValue(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter') submit(); }}/>
        <button className="btn accent" disabled={!canSubmit} onClick={submit}>{conf.action}</button>
        <button className="btn ghost" disabled={busy} onClick={onCancel}>Cancel</button>
      </div>
      <div className="text-secondary t-small" style={{ marginTop: 8 }}>{conf.hint}</div>
    </div>
  );
}

function OrgTelegramCard() {
  const {
    phase, loginStep, syncJob, items, error,
    beginConnect, sendCode, verifyCode, verifyPassword, cancelConnect,
    syncNow, ingest, disconnect,
  } = useOrgTelegram();
  const [busy, setBusy] = useOtState(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' ? (
        loginStep === 'phone' ? 'Enter the phone number to connect…'
        : loginStep === 'code' ? 'Waiting for the login code…'
        : 'Waiting for the two-step password…')
    : phase === 'disconnected' ? 'Not connected'
    : jobActive ? 'Syncing'
    : emptyAwaiting ? 'No new chats to ingest'
    : awaiting ? 'Choose chats to ingest'
    : failed ? 'Last sync failed'
    : cancelled ? 'Last sync cancelled'
    : nothingNew ? (nothingNewSkipped > 0
        ? `No ingestable chats — ${nothingNewSkipped} skipped`
        : 'No new chats — everything already in memory')
    : doneCounts ? `${doneCounts.done || 0} chats in memory`
    : 'Connected';

  return (
    <div id="org-telegram-connector" className="card" style={{ padding: '18px 22px', marginTop: 24 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
        <VendorLogo vendor="telegram" size={36} radius={9}/>
        <div style={{ flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span className="t-h3">Telegram</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={beginConnect}>
            Connect Telegram
          </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 }}>
          Sign in with the Telegram account whose chats should become org
          knowledge — you choose exactly which chats to ingest, and new
          messages in those chats keep flowing in.
        </div>
      )}

      {phase === 'connecting' && (
        <OrgTelegramLoginForm step={loginStep} busy={busy}
          onSendCode={run(sendCode)} onVerifyCode={run(verifyCode)}
          onVerifyPassword={run(verifyPassword)} onCancel={cancelConnect}/>
      )}

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

      {jobActive && <OrgTelegramSyncProgress job={syncJob}/>}
      {awaiting && items && (
        <OrgTelegramChatPicker 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 OrgTelegramSection() {
  if (!osLive()) return null;
  return <OrgTelegramCard/>;
}

Object.assign(window, { OrgTelegramSection });
