// Nordbank — admin backoffice (scenario 2).
//
// Lists scenario='batch' applications, lets an operator either:
//   1. Export the selection to a CSV (downloads to the operator's machine,
//      flips status -> 'exported').
//   2. Upload the selection to the Sherpa SFTP/Azure blob target via the
//      Pages function /api/sherpa-batch-upload, flipping status -> 'uploaded'.
//
// Auth gates: <Admin/> calls /api/admin-session on mount. 401 -> /admin/login.
// The CSV building/downloading lives in lib-batch-csv.jsx so the admin page
// stays focused on orchestration.

// ---------------------------------------------------------------------------
// Top-level gate
// ---------------------------------------------------------------------------

function Admin() {
  const [status, setStatus] = React.useState('checking'); // checking | ok | denied

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await fetch('/api/admin-session', { credentials: 'include' });
        if (cancelled) return;
        if (res.ok) {
          setStatus('ok');
        } else {
          setStatus('denied');
        }
      } catch (e) {
        if (cancelled) return;
        console.error('[admin] session check failed', e);
        setStatus('denied');
      }
    })();
    return () => { cancelled = true; };
  }, []);

  React.useEffect(() => {
    if (status === 'denied') navigate('/admin/login');
  }, [status]);

  if (status !== 'ok') {
    return (
      <div className="min-h-screen flex items-center justify-center bg-surface">
        <div className="flex items-center gap-3 text-navy-500 text-sm">
          <Spinner color="navy" size={18} />
          <span>{status === 'denied' ? 'Redirecting…' : 'Checking session…'}</span>
        </div>
      </div>
    );
  }

  return (
    <AdminLayout>
      <AdminApplications />
    </AdminLayout>
  );
}

// ---------------------------------------------------------------------------
// Admin chrome
// ---------------------------------------------------------------------------

function AdminLayout({ children }) {
  async function logout() {
    try {
      await fetch('/api/admin-logout', { method: 'POST', credentials: 'include' });
    } catch (e) {
      console.warn('[admin] logout request failed', e);
    } finally {
      navigate('/admin/login');
    }
  }

  return (
    <div className="min-h-screen bg-surface flex flex-col">
      <header className="border-b hairline bg-white">
        <div className="max-w-7xl mx-auto px-4 md:px-6 h-14 flex items-center gap-3">
          <NordbankLogo />
          <span className="hidden sm:inline text-xs font-semibold tracking-[0.18em] uppercase text-navy-300">
            · Admin · Backoffice
          </span>
          <div className="flex-1" />
          <button
            type="button"
            onClick={logout}
            className="text-sm font-medium text-navy-700 hover:text-navy-900 px-3 py-1.5 rounded-lg hover:bg-navy-50"
          >
            Log out
          </button>
        </div>
      </header>
      <main className="flex-1">
        {children}
      </main>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Applications table
// ---------------------------------------------------------------------------

function defaultChecked(row) {
  // Pre-tick rows that haven't been uploaded yet so the operator can take
  // action on the obvious set without manual selection.
  return row.batch_status !== 'uploaded';
}

function AdminApplications() {
  const [apps, setApps] = React.useState(null); // null = loading
  const [selected, setSelected] = React.useState(() => new Set());
  const [busy, setBusy] = React.useState(null); // null | 'export' | 'upload'
  const [banner, setBanner] = React.useState(null); // {kind, text}

  const load = React.useCallback(async () => {
    try {
      const rows = await dbListBatchApplications();
      setApps(rows);
      setSelected(prev => {
        // Seed selection on first load (prev is empty Set); otherwise keep
        // any still-present user choices and drop ids that vanished.
        if (prev.size === 0) {
          const next = new Set();
          for (const r of rows) if (defaultChecked(r)) next.add(r.id);
          return next;
        }
        const present = new Set(rows.map(r => r.id));
        const next = new Set();
        for (const id of prev) if (present.has(id)) next.add(id);
        return next;
      });
    } catch (e) {
      console.error('[admin] failed to load applications', e);
      setApps([]);
      setBanner({ kind: 'error', text: 'Failed to load applications.' });
    }
  }, []);

  React.useEffect(() => { load(); }, [load]);

  function toggle(id) {
    setSelected(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  function toggleAll() {
    if (!apps) return;
    const allOn = apps.every(r => selected.has(r.id));
    if (allOn) {
      setSelected(new Set());
    } else {
      setSelected(new Set(apps.map(r => r.id)));
    }
  }

  function selectedApps() {
    if (!apps) return [];
    return apps.filter(r => selected.has(r.id));
  }

  async function onExport() {
    if (busy) return;
    const picks = selectedApps();
    if (picks.length === 0) {
      setBanner({ kind: 'error', text: 'Select at least one application to export.' });
      return;
    }
    if (typeof window.buildBatchCsv !== 'function' || typeof window.downloadBatchCsv !== 'function') {
      setBanner({ kind: 'error', text: 'CSV helper not loaded — refresh and try again.' });
      return;
    }
    setBusy('export');
    setBanner(null);
    try {
      const csv = window.buildBatchCsv(picks);
      const filename = csvFilename();
      window.downloadBatchCsv(filename, csv);
      await dbMarkApplicationsExported(picks.map(p => p.id));
      setBanner({ kind: 'success', text: `Exported ${picks.length} application${picks.length === 1 ? '' : 's'} to ${filename}.` });
      await load();
    } catch (e) {
      console.error('[admin] export failed', e);
      setBanner({ kind: 'error', text: 'Export failed: ' + (e && e.message ? e.message : 'unknown error') });
    } finally {
      setBusy(null);
    }
  }

  async function onUpload() {
    if (busy) return;
    const picks = selectedApps();
    if (picks.length === 0) {
      setBanner({ kind: 'error', text: 'Select at least one application to upload.' });
      return;
    }
    if (typeof window.buildBatchCsv !== 'function') {
      setBanner({ kind: 'error', text: 'CSV helper not loaded — refresh and try again.' });
      return;
    }
    setBusy('upload');
    setBanner(null);
    try {
      const csv = window.buildBatchCsv(picks);
      const res = await fetch('/api/sherpa-batch-upload', {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ csv, applicationCount: picks.length }),
      });
      if (res.status === 401) {
        navigate('/admin/login');
        return;
      }
      if (!res.ok) {
        let detail = `HTTP ${res.status}`;
        try {
          const body = await res.json();
          if (body && typeof body.error === 'string') detail = body.error;
        } catch (e) { /* not JSON */ }
        throw new Error(detail);
      }
      const body = await res.json();
      const filename = body && body.filename ? String(body.filename) : csvFilename();
      await dbMarkApplicationsUploaded(picks.map(p => p.id), filename);
      setBanner({ kind: 'success', text: `Uploaded ${picks.length} application${picks.length === 1 ? '' : 's'} as ${filename}.` });
      await load();
    } catch (e) {
      console.error('[admin] upload failed', e);
      setBanner({ kind: 'error', text: 'Upload failed: ' + (e && e.message ? e.message : 'unknown error') });
    } finally {
      setBusy(null);
    }
  }

  return (
    <div className="max-w-7xl mx-auto px-4 md:px-6 py-8">
      <div className="flex flex-col md:flex-row md:items-end md:justify-between gap-4">
        <div>
          <h1 className="text-2xl font-semibold tracking-tight text-navy-900">Batch applications</h1>
          <p className="mt-1 text-sm text-navy-500">
            Scenario 2 — signed loan applications waiting to be sent to Sherpa as a daily CSV batch.
          </p>
        </div>
        <div className="flex flex-wrap items-center gap-3">
          <button
            type="button"
            onClick={onExport}
            disabled={!!busy || !apps || apps.length === 0}
            className="inline-flex items-center justify-center gap-2 bg-white border hairline text-navy-900 text-sm font-semibold px-4 py-2.5 rounded-full hover:bg-navy-50 disabled:opacity-50 disabled:cursor-not-allowed"
          >
            {busy === 'export' && <Spinner color="navy" size={14} />}
            Export Insurance to CSV
          </button>
          <button
            type="button"
            onClick={onUpload}
            disabled={!!busy || !apps || apps.length === 0}
            className="inline-flex items-center justify-center gap-2 bg-navy-900 hover:bg-navy-700 text-white text-sm font-semibold px-4 py-2.5 rounded-full shadow-card disabled:opacity-50 disabled:cursor-not-allowed"
          >
            {busy === 'upload' && <Spinner size={14} />}
            Upload Insurance to SFTP
          </button>
        </div>
      </div>

      {banner && <AdminBanner banner={banner} onDismiss={() => setBanner(null)} />}

      <div className="mt-6 bg-white border hairline rounded-2xl shadow-card overflow-hidden">
        {apps === null ? (
          <div className="p-10 flex items-center justify-center gap-3 text-navy-500 text-sm">
            <Spinner color="navy" size={18} />
            Loading applications…
          </div>
        ) : apps.length === 0 ? (
          <div className="p-10 text-center">
            <p className="text-navy-700 text-sm">No batch applications yet.</p>
            <p className="mt-1 text-xs text-navy-300">
              Submit one via <code className="font-mono">#/apply-batch</code> to populate this list.
            </p>
          </div>
        ) : (
          <AdminTable
            apps={apps}
            selected={selected}
            onToggle={toggle}
            onToggleAll={toggleAll}
          />
        )}
      </div>

      <p className="mt-4 text-[11px] text-navy-300">
        {apps && apps.length > 0 && (
          <>{selected.size} of {apps.length} selected · {apps.length} total</>
        )}
      </p>
    </div>
  );
}

function AdminTable({ apps, selected, onToggle, onToggleAll }) {
  const allOn = apps.length > 0 && apps.every(r => selected.has(r.id));
  return (
    <div className="overflow-x-auto">
      <table className="min-w-full text-sm">
        <thead className="bg-surface text-navy-500 text-xs uppercase tracking-wider">
          <tr>
            <th className="px-3 py-3 text-left w-10">
              <input
                type="checkbox"
                aria-label="Select all"
                checked={allOn}
                onChange={onToggleAll}
                className="accent-navy-900"
                style={{ accentColor: '#0B2545' }}
              />
            </th>
            <th className="px-3 py-3 text-left font-semibold">Confirmation #</th>
            <th className="px-3 py-3 text-left font-semibold">Name</th>
            <th className="px-3 py-3 text-left font-semibold">Email</th>
            <th className="px-3 py-3 text-left font-semibold">Country</th>
            <th className="px-3 py-3 text-right font-semibold">Amount</th>
            <th className="px-3 py-3 text-right font-semibold">Term</th>
            <th className="px-3 py-3 text-right font-semibold">Monthly premium</th>
            <th className="px-3 py-3 text-left font-semibold">Status</th>
            <th className="px-3 py-3 text-left font-semibold">Signed</th>
          </tr>
        </thead>
        <tbody className="divide-y hairline">
          {apps.map(row => (
            <AdminRow
              key={row.id}
              row={row}
              checked={selected.has(row.id)}
              onToggle={() => onToggle(row.id)}
            />
          ))}
        </tbody>
      </table>
    </div>
  );
}

function AdminRow({ row, checked, onToggle }) {
  const personal = (row.data && row.data.personal) || {};
  const amountStr = formatAmount(row.amount, row.currency);
  const premiumStr = formatAmount(row.insurance_premium || 0, row.currency, 2);
  const termStr = row.term_years === 1 ? '1 yr' : `${row.term_years} yrs`;
  const signedStr = formatTs(row.signed_at);
  return (
    <tr className={'hover:bg-surface/60 ' + (checked ? 'bg-teal-50/30' : '')}>
      <td className="px-3 py-3 align-middle">
        <input
          type="checkbox"
          aria-label={`Select ${row.confirmation_number}`}
          checked={checked}
          onChange={onToggle}
          className="accent-navy-900"
          style={{ accentColor: '#0B2545' }}
        />
      </td>
      <td className="px-3 py-3 align-middle font-mono text-[12px] text-navy-900 tnum">{row.confirmation_number}</td>
      <td className="px-3 py-3 align-middle text-navy-900">{personal.name || '—'}</td>
      <td className="px-3 py-3 align-middle text-navy-700 break-all">{personal.email || '—'}</td>
      <td className="px-3 py-3 align-middle text-navy-700">{row.country || '—'}</td>
      <td className="px-3 py-3 align-middle text-navy-900 tnum text-right">{amountStr}</td>
      <td className="px-3 py-3 align-middle text-navy-700 tnum text-right">{termStr}</td>
      <td className="px-3 py-3 align-middle text-navy-900 tnum text-right">{premiumStr}</td>
      <td className="px-3 py-3 align-middle">
        <StatusBadge status={row.batch_status} />
      </td>
      <td className="px-3 py-3 align-middle text-navy-500 text-xs tnum whitespace-nowrap">{signedStr}</td>
    </tr>
  );
}

function StatusBadge({ status }) {
  const cfg = (() => {
    switch (status) {
      case 'uploaded': return { label: 'Uploaded', cls: 'bg-success/10 text-success' };
      case 'exported': return { label: 'Exported', cls: 'bg-navy-100 text-navy-700' };
      case 'pending':  return { label: 'Pending',  cls: 'bg-amber-100 text-amber-700' };
      default:         return { label: 'Pending',  cls: 'bg-amber-100 text-amber-700' };
    }
  })();
  return (
    <span className={'inline-flex items-center px-2 py-0.5 rounded-md text-[11px] font-semibold tracking-wide uppercase ' + cfg.cls}>
      {cfg.label}
    </span>
  );
}

function AdminBanner({ banner, onDismiss }) {
  const isError = banner.kind === 'error';
  return (
    <div
      role={isError ? 'alert' : 'status'}
      className={'nb-slidein mt-6 flex items-start gap-3 rounded-xl px-4 py-3 border ' +
        (isError ? 'bg-danger/5 border-danger/20 text-danger' : 'bg-success/5 border-success/20 text-success')}
    >
      <span className="mt-0.5" aria-hidden="true">
        {isError ? (
          <svg width="16" height="16" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" stroke="currentColor" strokeWidth="1.5" fill="none"/><path d="M6 6 L10 10 M10 6 L6 10" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>
        ) : (
          <svg width="16" height="16" viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" stroke="currentColor" strokeWidth="1.5" fill="none"/><path d="M5 8 L7.2 10.2 L11 6" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
        )}
      </span>
      <p className="flex-1 text-sm font-medium">{banner.text}</p>
      <button
        type="button"
        onClick={onDismiss}
        className="text-xs font-semibold opacity-80 hover:opacity-100 px-2 py-0.5"
        aria-label="Dismiss"
      >
        Dismiss
      </button>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Local formatters — kept tiny and dependency-free so this page works even
// before lib-batch-csv.jsx has loaded.
// ---------------------------------------------------------------------------

function formatAmount(value, currency, fraction) {
  const f = typeof fraction === 'number' ? fraction : 0;
  if (value === null || value === undefined) return '—';
  try {
    return new Intl.NumberFormat('en-GB', {
      style: 'currency',
      currency: currency || 'NOK',
      minimumFractionDigits: f,
      maximumFractionDigits: f,
    }).format(value);
  } catch (e) {
    return `${Number(value).toFixed(f)} ${currency || ''}`.trim();
  }
}

function formatTs(ms) {
  if (!ms) return '—';
  try {
    const d = new Date(ms);
    return d.toISOString().slice(0, 16).replace('T', ' ');
  } catch (e) {
    return '—';
  }
}

function csvFilename() {
  const d = new Date();
  const p = n => String(n).padStart(2, '0');
  return `nordbank-batch-${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}-${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}.csv`;
}

Object.assign(window, {
  Admin, AdminLayout, AdminApplications, AdminTable, AdminRow,
  StatusBadge, AdminBanner,
});
