/*
 * FleetOnboarding.jsx — Agency Fleet Onboarding dashboard (Phase 2)
 * v1.0.0 — 2026-06-09
 *
 * The SaaS surface for connecting a whole site portfolio in one pass. Wraps the
 * Phase-1 server endpoints (API v1.50.0.0.0):
 *   - POST   /account/api-keys         (key_type:'master', scopes:['fleet:provision']) — generate Fleet Key
 *   - GET    /account/api-keys                                                          — list (filter fleet keys)
 *   - DELETE /account/api-keys/:id                                                      — revoke Fleet Key
 *   - POST   /account/fleet/manifest   { urls[], label }                               — pre-register portfolio
 *   - GET    /account/fleet/status                                                     — claimed-vs-pending checklist
 *
 * Agency plan + owner/dev (enforced server-side). Below-plan / wrong-role calls
 * 402/403 → the page shows an upgrade/permission notice rather than erroring.
 *
 * Auth: window.WPSB.getToken(). Theme vars only (matches v2 tokens.css). IIFE +
 * window namespace. Mobile responsive. WCAG 2.1 AA. Generic placeholders only.
 */
(function () {
  'use strict';

  const { useState, useEffect, useCallback } = React;
  const tx = (k, v, f) => (window.WPSB && window.WPSB.t) ? window.WPSB.t(k, v, f) : (f != null ? f : k);

  /* THE ONE shared dialog shell (design/ActivationPrimitives.jsx §4e).
     Was a local fixed-overlay fork at z-index 9999 until the L12
     consolidation. ActivationPrimitives.jsx loads at index.html:645,
     this file at :766. */
  const SharedModal = window.WPSB.Activation.Modal;
  const RAILWAY = (window.WPSB && window.WPSB.RAILWAY) || 'https://wpsitebeam-railway-api-production.up.railway.app';

  function authHeaders() {
    const token = (window.WPSB && window.WPSB.getToken && window.WPSB.getToken()) || '';
    return { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' };
  }
  function fmtInt(n) { return n == null || isNaN(n) ? '\u2014' : Number(n).toLocaleString(); }
  function fmtDate(iso) {
    if (!iso) return '\u2014';
    try { return new Date(iso).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }); }
    catch { return '\u2014'; }
  }
  function copyText(t) {
    if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(t);
    const ta = document.createElement('textarea'); ta.value = t; ta.style.position = 'fixed'; ta.style.opacity = '0';
    document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); } catch (e) {} document.body.removeChild(ta);
    return Promise.resolve();
  }
  function isFleetKey(k) {
    return k && k.key_type === 'master' && Array.isArray(k.scopes) && k.scopes.indexOf('fleet:provision') !== -1;
  }

  /* ── shared card primitive (v2 glass card) ───────────────────────── */
  function Card(props) {
    return React.createElement('div', {
      style: Object.assign({
        background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: '14px',
        boxShadow: 'var(--shadow-sm)', padding: '20px 22px', marginBottom: '18px',
      }, props.style || {}),
    }, props.children);
  }
  function Eyebrow(props) {
    return React.createElement('div', { style: {
      fontFamily: 'var(--font-mono, monospace)', fontSize: '.7rem', letterSpacing: '.08em',
      textTransform: 'uppercase', color: 'var(--orange)', marginBottom: '6px', fontWeight: 600,
    } }, props.children);
  }
  function Stat(props) {
    return React.createElement('div', { style: { flex: '1 1 120px', minWidth: 110 } },
      React.createElement('div', { style: { fontSize: '1.9rem', fontWeight: 700, color: props.color || 'var(--text)', lineHeight: 1.1 } }, props.value),
      React.createElement('div', { style: { fontSize: '.78rem', color: 'var(--muted)', marginTop: 2 } }, props.label));
  }

  function FleetOnboarding() {
    const [status, setStatus]   = useState(null);
    const [keys, setKeys]       = useState([]);
    const [loading, setLoading] = useState(true);
    const [gateErr, setGateErr] = useState('');     // 402/403 upsell/permission
    const [err, setErr]         = useState('');
    const [busy, setBusy]       = useState(false);
    const [newKey, setNewKey]   = useState(null);   // plain key shown once
    const [savedAck, setSavedAck] = useState(false);
    const [manifest, setManifest] = useState('');
    const [manifestResult, setManifestResult] = useState(null);

    const load = useCallback(async () => {
      setErr(''); setGateErr('');
      try {
        const [sr, kr] = await Promise.all([
          fetch(`${RAILWAY}/account/fleet/status`, { headers: authHeaders() }),
          fetch(`${RAILWAY}/account/api-keys`, { headers: authHeaders() }),
        ]);
        if (sr.status === 402 || sr.status === 403) {
          const b = await sr.json().catch(() => ({}));
          setGateErr(b && b.error ? b.error : (sr.status === 402 ? tx('page.fleet.gate_plan', null, 'Fleet onboarding requires the Agency plan.') : tx('page.fleet.gate_role', null, 'Only an account owner or developer can manage fleet onboarding.')));
          setLoading(false); return;
        }
        if (sr.ok) { const sd = await sr.json(); setStatus(sd); }
        if (kr.ok) { const kd = await kr.json(); const arr = Array.isArray(kd) ? kd : (kd.keys || kd.data || []); setKeys(arr.filter(isFleetKey)); }
      } catch (e) { setErr(tx('page.fleet.load_err', null, 'Could not load fleet status. Check your connection and retry.')); }
      setLoading(false);
    }, []);

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

    async function generateKey() {
      setBusy(true); setErr('');
      try {
        const r = await fetch(`${RAILWAY}/account/api-keys`, {
          method: 'POST', headers: authHeaders(),
          body: JSON.stringify({ key_type: 'master', scopes: ['fleet:provision'], label: 'Fleet Key' }),
        });
        const b = await r.json().catch(() => ({}));
        if (r.status === 402 || r.status === 403) { setGateErr(b.error || tx('page.fleet.not_permitted', null, 'Not permitted on your plan/role.')); }
        else if (r.status === 409) { setErr(b.error || tx('page.fleet.key_exists', null, 'You already have an active Fleet Key. Revoke it before generating a new one.')); }
        else if (r.ok && b.plain_key) { setNewKey(b.plain_key); setSavedAck(false); await load(); }
        else { setErr(b.error || tx('page.fleet.key_gen_err', null, 'Could not generate a Fleet Key.')); }
      } catch (e) { setErr(tx('page.fleet.key_gen_net_err', null, 'Network error generating the Fleet Key.')); }
      setBusy(false);
    }

    async function revokeKey(id) {
      if (!window.confirm(tx('page.fleet.revoke_confirm', null, 'Revoke this Fleet Key? Already-connected sites keep working; you just can\u2019t add new ones with this key.'))) return;
      setBusy(true); setErr('');
      try {
        const r = await fetch(`${RAILWAY}/account/api-keys/${id}`, { method: 'DELETE', headers: authHeaders() });
        if (r.ok) await load(); else { const b = await r.json().catch(() => ({})); setErr(b.error || tx('page.fleet.revoke_err', null, 'Could not revoke the key.')); }
      } catch (e) { setErr(tx('page.fleet.revoke_net_err', null, 'Network error revoking the key.')); }
      setBusy(false);
    }

    async function submitManifest() {
      const urls = manifest.split(/[\s,]+/).map(s => s.trim()).filter(Boolean);
      if (!urls.length) { setErr(tx('page.fleet.manifest_empty', null, 'Paste at least one site URL.')); return; }
      setBusy(true); setErr(''); setManifestResult(null);
      try {
        const r = await fetch(`${RAILWAY}/account/fleet/manifest`, {
          method: 'POST', headers: authHeaders(), body: JSON.stringify({ urls, label: 'Portfolio manifest' }),
        });
        const b = await r.json().catch(() => ({}));
        if (r.status === 402 || r.status === 403) { setGateErr(b.error || tx('page.fleet.not_permitted', null, 'Not permitted on your plan/role.')); }
        else if (r.ok) { setManifestResult(b); await load(); }
        else { setErr(b.error || tx('page.fleet.manifest_err', null, 'Could not save the manifest.')); }
      } catch (e) { setErr(tx('page.fleet.manifest_net_err', null, 'Network error saving the manifest.')); }
      setBusy(false);
    }

    const btnPrimary = { padding: '10px 18px', borderRadius: 10, border: '1px solid var(--orange)', background: 'var(--orange)', color: 'var(--cta-text, #000)', fontWeight: 600, cursor: 'pointer', fontSize: '.9rem' };
    const btnGhost   = { padding: '8px 14px', borderRadius: 9, border: '1px solid var(--border-2)', background: 'transparent', color: 'var(--text)', cursor: 'pointer', fontSize: '.85rem' };
    const btnDanger  = { padding: '6px 12px', borderRadius: 8, border: '1px solid color-mix(in oklab, var(--red) 50%, transparent)', background: 'var(--red-dim)', color: 'var(--red)', cursor: 'pointer', fontSize: '.8rem' };

    if (loading) {
      return React.createElement('div', { style: { padding: 24, color: 'var(--muted)' } }, tx('page.fleet.loading', null, 'Loading fleet onboarding\u2026'));
    }

    if (gateErr) {
      return React.createElement('div', { style: { maxWidth: 760, margin: '0 auto', padding: '8px 4px' } },
        React.createElement('h1', { style: { color: 'var(--text)', fontSize: '1.5rem', marginBottom: 4 } }, tx('page.fleet.title', null, 'Fleet Onboarding')),
        React.createElement(Card, { style: { borderLeft: '4px solid var(--warn)' } },
          React.createElement('div', { style: { fontWeight: 600, color: 'var(--text)', marginBottom: 6 } }, tx('page.fleet.gate_heading', null, 'Available on the Agency plan')),
          React.createElement('p', { style: { color: 'var(--text-2)', margin: '0 0 12px', lineHeight: 1.5 } }, gateErr),
          React.createElement('a', { href: '#billing', onClick: function (e) { e.preventDefault(); if (window.WPSB && window.WPSB.go) window.WPSB.go('billing'); }, style: Object.assign({ textDecoration: 'none', display: 'inline-block' }, btnPrimary) }, tx('page.fleet.view_plans', null, 'View plans'))));
    }

    const total = status ? status.total_fleet_sites : 0;
    const claimed = status ? status.claimed : 0;
    const pending = status ? status.pending : 0;

    return React.createElement('div', { style: { maxWidth: 880, margin: '0 auto', padding: '8px 4px' } },
      React.createElement('h1', { style: { color: 'var(--text)', fontSize: '1.5rem', marginBottom: 2 } }, tx('page.fleet.title', null, 'Fleet Onboarding')),
      React.createElement('p', { style: { color: 'var(--muted)', margin: '0 0 18px', maxWidth: 640, lineHeight: 1.5 } },
        tx('page.fleet.intro', null, 'Connect your whole portfolio in one pass. Each site claims its own private credential \u2014 the Fleet Key only provisions and is never stored as a site login.')),

      err && React.createElement('div', { style: { background: 'var(--red-dim)', border: '1px solid color-mix(in oklab, var(--red) 40%, transparent)', color: 'var(--red)', borderRadius: 10, padding: '10px 14px', marginBottom: 16, fontSize: '.88rem' } }, err),

      /* ── Live status ─────────────────────────────────────────── */
      React.createElement(Card, null,
        React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 14 } },
          React.createElement(Eyebrow, null, tx('page.fleet.status_eyebrow', null, 'Portfolio status')),
          React.createElement('button', { onClick: load, disabled: busy, style: btnGhost }, tx('page.fleet.refresh', null, 'Refresh'))),
        React.createElement('div', { style: { display: 'flex', gap: 16, flexWrap: 'wrap' } },
          React.createElement(Stat, { value: fmtInt(total), label: tx('page.fleet.stat_sites', null, 'Fleet sites') }),
          React.createElement(Stat, { value: fmtInt(claimed), label: tx('page.fleet.stat_connected', null, 'Connected'), color: 'var(--green)' }),
          React.createElement(Stat, { value: fmtInt(pending), label: tx('page.fleet.stat_waiting', null, 'Waiting'), color: 'var(--warn)' }),
          React.createElement(Stat, { value: fmtDate(status && status.last_claim_at), label: tx('page.fleet.stat_last_connect', null, 'Last connect') })),
        total > 0 && React.createElement('div', { style: { marginTop: 14, height: 8, borderRadius: 6, background: 'var(--surface-3)', overflow: 'hidden' } },
          React.createElement('div', { style: { height: '100%', width: (claimed / Math.max(total, 1) * 100) + '%', background: 'var(--green)', transition: 'width .4s' } }))),

      /* ── Step 1: Fleet Key ───────────────────────────────────── */
      React.createElement(Card, null,
        React.createElement(Eyebrow, null, tx('page.fleet.step1_eyebrow', null, 'Step 1 \u00b7 Fleet Key')),
        React.createElement('p', { style: { color: 'var(--text-2)', margin: '0 0 14px', lineHeight: 1.5, fontSize: '.9rem' } },
          tx('page.fleet.step1_desc', null, 'Generate one Fleet Key, drop it on your sites, and each site connects itself. The key is provisioning-only \u2014 it cannot read or change any site\u2019s data, and you can revoke it any time.')),
        keys.length === 0
          ? React.createElement('button', { onClick: generateKey, disabled: busy, style: btnPrimary }, busy ? tx('page.fleet.working', null, 'Working\u2026') : tx('page.fleet.generate_key', null, 'Generate Fleet Key'))
          : React.createElement('div', null, keys.map(function (k) {
              return React.createElement('div', { key: k.id, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '10px 12px', border: '1px solid var(--border)', borderRadius: 10, marginBottom: 8 } },
                React.createElement('div', null,
                  React.createElement('code', { style: { color: 'var(--text)', fontSize: '.85rem' } }, (k.key_prefix || 'wpsb_live_') + '\u2026'),
                  React.createElement('span', { style: { color: 'var(--dim)', fontSize: '.75rem', marginLeft: 10 } }, tx('page.fleet.provisioning_only', null, 'provisioning-only'))),
                React.createElement('button', { onClick: function () { revokeKey(k.id); }, disabled: busy, style: btnDanger }, tx('page.fleet.revoke', null, 'Revoke')));
            }))),

      /* ── Step 2: Manifest ────────────────────────────────────── */
      React.createElement(Card, null,
        React.createElement(Eyebrow, null, tx('page.fleet.step2_eyebrow', null, 'Step 2 (optional) \u00b7 Your site list')),
        React.createElement('p', { style: { color: 'var(--text-2)', margin: '0 0 10px', lineHeight: 1.5, fontSize: '.9rem' } },
          tx('page.fleet.step2_desc', null, 'Paste your site URLs (one per line, or comma-separated). This gives you the live checklist above and restricts connections to only the sites you list.')),
        React.createElement('textarea', {
          value: manifest, onChange: function (e) { setManifest(e.target.value); },
          placeholder: tx('page.fleet.manifest_placeholder', null, 'https://client-one.com\nhttps://client-two.org\nhttps://client-three.net'),
          rows: 5, style: { width: '100%', boxSizing: 'border-box', background: 'var(--surface-2)', color: 'var(--text)', border: '1px solid var(--border)', borderRadius: 10, padding: '10px 12px', fontFamily: 'var(--font-mono, monospace)', fontSize: '.85rem', resize: 'vertical' } }),
        React.createElement('div', { style: { marginTop: 10 } },
          React.createElement('button', { onClick: submitManifest, disabled: busy, style: btnGhost }, busy ? tx('common.saving', null, 'Saving\u2026') : tx('page.fleet.save_site_list', null, 'Save site list'))),
        manifestResult && React.createElement('div', { style: { marginTop: 12, fontSize: '.85rem', color: 'var(--text-2)' } },
          React.createElement('span', { style: { color: 'var(--green)' } }, tx('page.fleet.manifest_valid', { n: fmtInt(manifestResult.expected_count) }, '{n} valid')),
          tx('page.fleet.manifest_summary', { claimed: fmtInt(manifestResult.already_claimed_count), pending: fmtInt(manifestResult.pending_registered) }, ' \u00b7 {claimed} already connected \u00b7 {pending} added to checklist'),
          (manifestResult.invalid && manifestResult.invalid.length) ? React.createElement('div', { style: { color: 'var(--warn)', marginTop: 4 } }, tx('page.fleet.manifest_skipped', { n: fmtInt(manifestResult.invalid.length) }, '{n} skipped (invalid URL)')) : null)),

      /* ── Step 3: Run ─────────────────────────────────────────── */
      React.createElement(Card, null,
        React.createElement(Eyebrow, null, tx('page.fleet.step3_eyebrow', null, 'Step 3 \u00b7 Connect your sites')),
        React.createElement('p', { style: { color: 'var(--text-2)', margin: '0 0 10px', lineHeight: 1.5, fontSize: '.9rem' } },
          tx('page.fleet.step3_desc_pre', null, 'Run this once from a machine with WP-CLI access to your sites (replace the key and the list). On WordPress Multisite, use '), React.createElement('strong', null, tx('page.fleet.step3_multisite_path', null, 'Network Admin \u2192 WPSiteBeam \u2192 Connect all')), tx('page.fleet.step3_desc_post', null, ' instead.')),
        React.createElement('pre', { style: { background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 10, padding: '12px 14px', overflow: 'auto', fontSize: '.8rem', color: 'var(--text)', lineHeight: 1.55 } },
'for SITE in @client1 @client2 /var/www/client3; do\n  wp --ssh="$SITE" plugin install wpsitebeam --activate \\\n    && wp --ssh="$SITE" wpsb fleet-connect --key="' + (newKey || (keys[0] && (keys[0].key_prefix + '\u2026')) || 'wpsb_live_\u2026') + '"\ndone'),
        React.createElement('p', { style: { color: 'var(--dim)', fontSize: '.78rem', margin: '8px 0 0' } },
          tx('page.fleet.step3_note', null, 'Sites connect independently \u2014 one failure never stops the rest. Re-running is safe; connected sites are skipped.'))),

      /* ── one-time key reveal modal ───────────────────────────── */
      /* One-time key reveal. Escape and scrim-click are DELIBERATELY NOT
         wired (scrimClose false + no-op onClose + showClose false): the
         key is shown exactly once and the acknowledgement checkbox is the
         only way past it, so an accidental dismissal loses the secret
         irrecoverably. That was this fork's behaviour before the L12
         consolidation — it had no close affordance and no key handler —
         and it is preserved deliberately rather than by omission. */
      newKey && React.createElement(SharedModal, {
        ariaLabel: tx('page.fleet.reveal_title', null, 'Save your Fleet Key now'),
        onClose: function () {}, scrimClose: false, showClose: false,
        width: 520, bodyPad: 24,
      },
        React.createElement('div', null,
          React.createElement('h2', { style: { color: 'var(--text)', fontSize: '1.15rem', margin: '0 0 6px' } }, tx('page.fleet.reveal_title', null, 'Save your Fleet Key now')),
          React.createElement('p', { style: { color: 'var(--text-2)', fontSize: '.88rem', margin: '0 0 14px', lineHeight: 1.5 } }, tx('page.fleet.reveal_desc', null, 'This is shown once. If you lose it, revoke and generate a new one.')),
          React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', marginBottom: 16 } },
            React.createElement('code', { style: { flex: 1, background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 8, padding: '10px 12px', color: 'var(--text)', fontSize: '.82rem', wordBreak: 'break-all' } }, newKey),
            React.createElement('button', { onClick: function () { copyText(newKey); }, style: btnGhost }, tx('page.fleet.copy', null, 'Copy'))),
          React.createElement('label', { style: { display: 'flex', gap: 8, alignItems: 'center', color: 'var(--text-2)', fontSize: '.85rem', marginBottom: 16, cursor: 'pointer' } },
            React.createElement('input', { type: 'checkbox', checked: savedAck, onChange: function (e) { setSavedAck(e.target.checked); } }),
            tx('page.fleet.reveal_ack', null, 'I\u2019ve saved this key somewhere safe')),
          React.createElement('button', { onClick: function () { if (savedAck) setNewKey(null); }, disabled: !savedAck, style: Object.assign({}, btnPrimary, { width: '100%', opacity: savedAck ? 1 : .5, cursor: savedAck ? 'pointer' : 'not-allowed' }) }, tx('common.done', null, 'Done')))));
  }

  window.FleetOnboarding = FleetOnboarding;
})();
