// API Keys screen — integration partners generate, rotate and revoke the
// credentials their integration uses against the Bookable API.
//
// Two sections, one per environment (BOO-95) — both real, backed by
// /api/partner-keys?env=<production|sandbox> → Bookable /partners/{id}/keys on
// that env's host (api.bookabletech.com / api-sandbox.bookabletech.com):
//   • Production — keys for the live API. Calls hit real venues/bookings.
//   • Sandbox — keys for the sandbox API. Safe test data.
// The clientSecret is shown ONCE on creation; the Client ID is masked in the
// list with a reveal toggle.
//
// The whole screen is gated on `keysEnabled` (the signed-in user is linked to a
// partner). Without it we show an unavailable state rather than empty sections.

// One real driver shape { list, create, remove } per Bookable environment.
// Both production and sandbox hit the live Bookable API for that env (server
// routes by ?env); the secret is returned once on create.
function ppKeyDriver(env) {
  return {
    async list() { return ppLoadPartnerKeys(env); },
    // Returns { key, rotateError } — rotateError is set when the replacement
    // key was minted but the old one couldn't be revoked (non-atomic rotate).
    async create(name, rotateOf) {
      const res = await ppCreatePartnerKey({ name }, rotateOf, env);
      const key = res && res.key ? res.key : res;
      return { key, rotateError: (res && res.rotateError) || null };
    },
    async rename(clientId, name) { return ppRenamePartnerKey(clientId, name, env); },
    async remove(clientId) { return ppDeletePartnerKey(clientId, env); },
  };
}

// Settings screen. Developer-facing configuration is grouped under a
// "For developers" section with tabs: API Keys (credential management) and
// Catalogue Feed (bulk availability files).
function SettingsScreen({ keysEnabled, canManageKeys = true, userEmail }) {
  // Default to the feed tab when this account can't manage keys (operator-only,
  // or a DistributorBookingOnly partner without partner:read — BOO-412).
  const [tab, setTab] = React.useState(keysEnabled ? 'keys' : 'feed');
  return (
    <div className="pp-screen-keys">
      <div className="pp-page-head">
        <div>
          <h1 className="pp-page-display">Settings</h1>
          <p className="pp-page-sub">Preferences and the credentials your integration uses to call the Bookable API.</p>
        </div>
      </div>

      <section className="pp-settings-group">
        <header className="pp-settings-group-head">
          <h2 className="pp-settings-group-title">Preferences</h2>
          <p className="pp-muted pp-settings-group-sub">How the portal looks and behaves on this device.</p>
        </header>
        <div className="pp-keys-sections">
          <InstallSettings/>
          <LangSettings/>
        </div>
      </section>

      {/* BOO-412: DistributorBookingOnly users (own bookings only, no
          partner:read) get no developer tooling at all — no sandbox, keys or feed. */}
      {canManageKeys && (
      <section className="pp-settings-group">
        <header className="pp-settings-group-head">
          <h2 className="pp-settings-group-title">For developers</h2>
          <p className="pp-muted pp-settings-group-sub">
            Sandbox testing, the credentials your integration uses to call the Bookable API, and the catalogue feed for bulk availability.
          </p>
        </header>

        <div className="pp-keys-sections" style={{ marginBottom: 18 }}>
          <SandboxModeSettings/>
        </div>

        {/* No API Keys tab when the account can't manage keys — show just the
            feed rather than an orphan one-tab switcher (BOO-412). */}
        {keysEnabled && (
          <div className="pp-segmented" role="tablist" aria-label="Developer settings" style={{ marginBottom: 18 }}>
            <button className={"pp-segmented-btn" + (tab === 'keys' ? ' is-active' : '')}
                    role="tab" aria-selected={tab === 'keys'} onClick={() => setTab('keys')}>
              <KeyGlyph size={13}/> API Keys
            </button>
            <button className={"pp-segmented-btn" + (tab === 'feed' ? ' is-active' : '')}
                    role="tab" aria-selected={tab === 'feed'} onClick={() => setTab('feed')}>
              Catalogue Feed
            </button>
          </div>
        )}

        {keysEnabled && tab === 'keys' ? (
          <div className="pp-keys-sections">
            <KeySection
              env="production"
              title="Production API keys"
              subtitle="Live keys. Calls hit real venues, bookings and customers — keep these secret."
              userEmail={userEmail}/>
            <KeySection
              env="sandbox"
              title="Sandbox API keys"
              subtitle="Test keys against sandbox data. Safe to experiment with."
              userEmail={userEmail}/>
          </div>
        ) : (
          // Feed files are public — works even when this sign-in can't manage keys.
          <div className="pp-keys-sections">
            <CatalogueFeedSection/>
          </div>
        )}
      </section>
      )}
    </div>
  );
}

// Sandbox mode toggle. Flips the whole portal between the live Bookable API and
// the sandbox environment (test bookings + availability). ppSetSandbox persists
// the flag and reloads so boot re-pulls data from the chosen env; a banner
// shows across the top while it's on (see SandboxBanner).
function SandboxModeSettings() {
  const on = !!(window.ppSandboxOn && window.ppSandboxOn());
  return (
    <section className="pp-keys-section">
      <div className="pp-keys-section-head">
        <div className="pp-keys-section-title">
          <span className={"pp-env-pill" + (on ? " pp-env-pill--sandbox" : "")}>
            {on && <span className="pp-env-dot"/>}Sandbox mode
          </span>
        </div>
        <p className="pp-keys-section-sub">
          Point the whole portal at the Bookable sandbox — see your sandbox bookings and make test
          bookings without touching live data. Switching reloads the portal.
        </p>
      </div>
      <div className="pp-switch-row">
        <button type="button" role="switch" aria-checked={on}
                className={"pp-switch" + (on ? " is-on" : "")}
                onClick={() => window.ppSetSandbox(!on)}>
          <span className="pp-switch-knob"/>
        </button>
        <span className="pp-switch-label">
          {on ? 'On — using sandbox data' : 'Off — using production data'}
        </span>
      </div>
    </section>
  );
}

// Install-the-app section. Browsers don't reliably offer PWA install on
// their own (iOS never does; Chrome hides it behind engagement heuristics),
// so we surface it ourselves:
//   • Chrome/Edge/Android — consume the stashed beforeinstallprompt event
//     (captured in index.html) and call prompt() from a button.
//   • iOS Safari — no API exists; show Share → Add to Home Screen steps.
//   • Already installed / running standalone — render nothing.
function usePwaInstall() {
  const standalone =
    (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) ||
    window.navigator.standalone === true;
  const [promptEvt, setPromptEvt] = React.useState(window.__pp_install_prompt);
  const [installed, setInstalled] = React.useState(false);

  React.useEffect(() => {
    const onAvail = () => setPromptEvt(window.__pp_install_prompt);
    const onDone = () => { setPromptEvt(null); setInstalled(true); };
    window.addEventListener('pp-installable', onAvail);
    window.addEventListener('pp-installed', onDone);
    return () => {
      window.removeEventListener('pp-installable', onAvail);
      window.removeEventListener('pp-installed', onDone);
    };
  }, []);

  const isIos = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
    (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);

  const install = () => {
    const e = promptEvt;
    if (!e) return;
    setPromptEvt(null);
    e.prompt();
  };

  return { standalone, promptEvt, installed, isIos, install };
}

// One-time dismissible install banner on the search landing screen. Renders
// only where install is actually actionable (a stashed prompt event, or iOS
// where the Share-sheet steps are the only route). Marked seen as soon as it
// renders, so it shows on one visit only — Settings keeps the permanent entry.
function InstallBanner() {
  const { standalone, promptEvt, installed, isIos, install } = usePwaInstall();
  const [dismissed, setDismissed] = React.useState(() => {
    try { return localStorage.getItem('pp_install_banner_seen') === '1'; }
    catch (e) { return true; }
  });

  const actionable = !standalone && !installed && (!!promptEvt || isIos);

  React.useEffect(() => {
    if (!dismissed && actionable) {
      try { localStorage.setItem('pp_install_banner_seen', '1'); } catch (e) { /* private mode */ }
    }
  }, [dismissed, actionable]);

  if (dismissed || !actionable) return null;

  return (
    <div className="pp-install-banner" role="status">
      <img className="pp-install-banner-icon" src="assets/bookable-icon.png" alt=""/>
      <div className="pp-install-banner-text">
        <strong>Install the app</strong>
        <span>
          {promptEvt
            ? 'Full screen, straight from your home screen.'
            : 'In Safari: tap Share, then “Add to Home Screen”.'}
        </span>
      </div>
      {promptEvt && (
        <button className="pp-btn pp-btn--primary pp-btn--xs"
                onClick={() => { install(); setDismissed(true); }}>
          Install
        </button>
      )}
      <button className="pp-icon-btn" aria-label="Dismiss"
              onClick={() => setDismissed(true)}>
        <IconClose size={14}/>
      </button>
    </div>
  );
}

function InstallSettings() {
  const { standalone, promptEvt, installed, isIos, install } = usePwaInstall();

  if (standalone) return null;

  return (
    <section className="pp-keys-section">
      <div className="pp-keys-section-head">
        <div className="pp-keys-section-title">
          <span className="pp-env-pill">Install the app</span>
        </div>
        <p className="pp-keys-section-sub">
          Put the portal on your home screen — opens full screen, no browser chrome.
        </p>
      </div>
      {installed ? (
        <div className="pp-banner-success">Installed — find it on your home screen.</div>
      ) : promptEvt ? (
        <div>
          <button className="pp-btn pp-btn--primary" onClick={install}>Install app</button>
        </div>
      ) : isIos ? (
        <p className="pp-keys-section-sub" style={{ margin: 0 }}>
          In Safari: tap <strong>Share</strong>, then <strong>Add to Home Screen</strong>.
        </p>
      ) : (
        <p className="pp-keys-section-sub" style={{ margin: 0 }}>
          In your browser menu, choose <strong>Install app</strong> (Chrome and Edge show it
          after you’ve used the portal for a moment).
        </p>
      )}
    </section>
  );
}

function KeySection({ env, title, subtitle, userEmail }) {
  const driver = React.useMemo(() => ppKeyDriver(env), [env]);
  const [keys, setKeys] = React.useState(null);   // null = loading, [] = none
  const [error, setError] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [confirm, setConfirm] = React.useState(null); // {mode:'generate'|'rotate', clientId?}
  const [secret, setSecret] = React.useState(null);   // freshly minted key (one-time)
  const [revoke, setRevoke] = React.useState(null);   // clientId pending revoke
  const [editing, setEditing] = React.useState(null); // clientId being renamed
  const [draftName, setDraftName] = React.useState('');

  const reload = React.useCallback(async () => {
    setError(null);
    try {
      const list = await driver.list();
      setKeys(Array.isArray(list) ? list : []);
    } catch (e) {
      setKeys([]);
      setError(e?.status === 403 ? 'You don’t have permission to manage these keys.'
        : e?.status === 401 ? 'Bookable rejected this session for key management. Your sign-in is fine — this is a key-service problem, not an expired session.'
        : (e?.message || 'Could not load keys.'));
    }
  }, [env]);

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

  const doCreate = async (name, rotateOf) => {
    setBusy(true);
    try {
      const { key, rotateError } = await driver.create(name || (env === 'sandbox' ? 'Sandbox key' : 'Production key'), rotateOf);
      setConfirm(null);
      setSecret(key);
      await reload();
      // reload() clears any prior error first, so flag a failed rotate revoke
      // AFTER it — the old key is still live and needs manual revocation.
      if (rotateError) setError('New key created — but the previous key couldn’t be revoked. It’s still live; revoke it manually below.');
    } catch (e) {
      setConfirm(null);
      setError(e?.message || 'Could not generate the key.');
    } finally { setBusy(false); }
  };

  const doRevoke = async (clientId) => {
    setBusy(true);
    try { await driver.remove(clientId); setRevoke(null); await reload(); }
    catch (e) { setRevoke(null); setError(e?.message || 'Could not revoke the key.'); }
    finally { setBusy(false); }
  };

  const doRename = async (clientId) => {
    const name = draftName.trim();
    if (!name) return;
    setBusy(true);
    try { await driver.rename(clientId, name); setEditing(null); await reload(); }
    catch (e) { setError(e?.message || 'Could not rename the key.'); }
    finally { setBusy(false); }
  };

  const hasKeys = Array.isArray(keys) && keys.length > 0;

  return (
    <section className="pp-keys-section">
      <header className="pp-keys-section-head">
        <div className="pp-keys-section-title">
          <span className={"pp-env-pill pp-env-pill--" + env}>
            <span className="pp-env-dot"/>{title}
          </span>
        </div>
        <p className="pp-keys-section-sub">{subtitle}</p>
      </header>

      {error && (
        <div className="pp-banner-warn"><AlertGlyph size={14}/><span>{error}</span></div>
      )}

      {keys === null ? (
        <div className="pp-key-card pp-key-card--loading"><span className="pp-muted" style={{ fontSize: 13 }}>Loading…</span></div>
      ) : !hasKeys ? (
        <div className="pp-key-card pp-key-card--empty">
          <div className="pp-key-empty-text">No key generated yet.</div>
          <button className="pp-btn pp-btn--primary" disabled={busy}
                  onClick={() => setConfirm({ mode: 'generate' })}>
            <KeyGlyph size={14}/> Generate key
          </button>
        </div>
      ) : (
        keys.map(k => (
          <div key={k.clientId} className="pp-key-card">
            <div className="pp-key-card-main">
              {editing === k.clientId ? (
                <form className="pp-key-rename" onSubmit={(e) => { e.preventDefault(); doRename(k.clientId); }}>
                  <input className="pp-input" value={draftName} autoFocus maxLength={255} disabled={busy}
                         placeholder={env === 'sandbox' ? 'Sandbox key' : 'Production key'}
                         onChange={(e) => setDraftName(e.target.value)}
                         onKeyDown={(e) => { if (e.key === 'Escape') setEditing(null); }}/>
                  <button type="submit" className="pp-btn pp-btn--primary pp-btn--xs" disabled={busy || !draftName.trim()}>Save</button>
                  <button type="button" className="pp-btn pp-btn--ghost pp-btn--xs" disabled={busy} onClick={() => setEditing(null)}>Cancel</button>
                </form>
              ) : (
                <div className="pp-key-card-name">
                  <span>{k.name || (env === 'sandbox' ? 'Sandbox key' : 'Production key')}</span>
                  <button type="button" className="pp-icon-btn pp-key-rename-btn" aria-label="Rename key" disabled={busy}
                          onClick={() => { setEditing(k.clientId); setDraftName(k.name || ''); }}>
                    <PencilGlyph size={13}/>
                  </button>
                </div>
              )}
              <CopyField label="Client ID" value={k.clientId} mask/>
              {k.clientSecret
                ? <CopyField label="Client Secret" value={k.clientSecret} mask secret/>
                : <p className="pp-key-secret-note">Client secret unavailable for this key — <strong>Rotate</strong> to generate a new one.</p>}
              <div className="pp-key-meta">
                <span>Created {fmtKeyDate(k.dateCreated)}</span>
                {k.lastRotatedBy && <span>· Last rotated by {k.lastRotatedBy} on {fmtKeyDate(k.lastRotatedAt)}</span>}
              </div>
            </div>
            <div className="pp-key-card-actions">
              <button className="pp-btn pp-btn--ghost" disabled={busy}
                      onClick={() => setConfirm({ mode: 'rotate', clientId: k.clientId, name: k.name })}>
                <RotateGlyph size={13}/> Rotate
              </button>
              <button className="pp-btn pp-btn--ghost pp-btn--danger-ghost" disabled={busy}
                      onClick={() => setRevoke(k.clientId)}>
                Revoke
              </button>
            </div>
          </div>
        ))
      )}

      {confirm && (
        <ConfirmKeyModal mode={confirm.mode} env={env} busy={busy}
          onCancel={() => setConfirm(null)}
          onConfirm={() => doCreate(confirm.name, confirm.mode === 'rotate' ? confirm.clientId : null)}/>
      )}
      {secret && (
        <SecretModal env={env} keyData={secret} onClose={() => setSecret(null)}/>
      )}
      {revoke && (
        <RevokeKeyModal busy={busy} onCancel={() => setRevoke(null)} onConfirm={() => doRevoke(revoke)}/>
      )}
    </section>
  );
}

// One-time secret reveal — Client ID + Client Secret with copy buttons.
function SecretModal({ env, keyData, onClose }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <React.Fragment>
      <div className="pp-modal-scrim" onClick={onClose}/>
      <div className="pp-modal pp-modal--wide" role="dialog" aria-label="Your new API key">
        <div className="pp-modal-icon"><KeyGlyph size={18}/></div>
        <h3 className="pp-modal-title">Your new {env === 'sandbox' ? 'sandbox' : 'production'} key</h3>
        <div className="pp-banner-warn" style={{ marginBottom: 4 }}>
          <AlertGlyph size={14}/>
          <span>Copy your Client Secret and keep it safe — treat it like a password.</span>
        </div>
        <CopyField label="Client ID" value={keyData.clientId}/>
        <CopyField label="Client Secret" value={keyData.clientSecret} secret/>
        <div className="pp-modal-actions">
          <button className="pp-btn pp-btn--primary" onClick={onClose}>Done</button>
        </div>
      </div>
    </React.Fragment>
  );
}

function ConfirmKeyModal({ mode, env, busy, onConfirm, onCancel }) {
  const rotate = mode === 'rotate';
  return (
    <React.Fragment>
      <div className="pp-modal-scrim" onClick={() => !busy && onCancel()}/>
      <div className="pp-modal" role="dialog" aria-label={rotate ? 'Rotate key' : 'Generate key'}>
        <div className={"pp-modal-icon" + (rotate ? " pp-modal-icon--danger" : "")}>
          {rotate ? <RotateGlyph size={18}/> : <KeyGlyph size={18}/>}
        </div>
        <h3 className="pp-modal-title">{rotate ? 'Rotate this secret?' : 'Generate a new key?'}</h3>
        <p className="pp-modal-sub">
          {rotate
            ? 'Rotating immediately invalidates the current secret. Integrations using it will stop working until you update them.'
            : `A new ${env} Client ID and Client Secret will be created. The secret is shown only once.`}
        </p>
        <div className="pp-modal-actions">
          <button className="pp-btn pp-btn--ghost" onClick={onCancel} disabled={busy}>Cancel</button>
          <button className={"pp-btn " + (rotate ? "pp-btn--danger" : "pp-btn--primary")} onClick={onConfirm} disabled={busy}>
            {busy ? 'Working…' : rotate ? 'Yes, rotate secret' : 'Generate key'}
          </button>
        </div>
      </div>
    </React.Fragment>
  );
}

function RevokeKeyModal({ busy, onConfirm, onCancel }) {
  return (
    <React.Fragment>
      <div className="pp-modal-scrim" onClick={() => !busy && onCancel()}/>
      <div className="pp-modal" role="dialog" aria-label="Revoke key">
        <div className="pp-modal-icon pp-modal-icon--danger"><AlertGlyph size={18}/></div>
        <h3 className="pp-modal-title">Revoke this key?</h3>
        <p className="pp-modal-sub">The key stops working immediately and can’t be restored. Any integration using it will break.</p>
        <div className="pp-modal-actions">
          <button className="pp-btn pp-btn--ghost" onClick={onCancel} disabled={busy}>Keep key</button>
          <button className="pp-btn pp-btn--danger" onClick={onConfirm} disabled={busy}>{busy ? 'Revoking…' : 'Yes, revoke'}</button>
        </div>
      </div>
    </React.Fragment>
  );
}

// `mask` hides the value behind dots until the user reveals it (the value is
// no secret — the secret is shown once on create — but partners asked not to
// have their Client ID on screen by default). Copy works without revealing.
function CopyField({ label, value, secret, mask }) {
  const [copied, setCopied] = React.useState(false);
  const [revealed, setRevealed] = React.useState(!mask);
  const copy = async () => {
    try {
      await navigator.clipboard.writeText(value);
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    } catch (e) { /* clipboard blocked — value is still selectable */ }
  };
  const shown = revealed ? value : '•'.repeat(Math.min(String(value || '').length, 28));
  return (
    <div className="pp-copy-field">
      <span className="pp-copy-field-label">{label}</span>
      <div className="pp-copy-field-row">
        <code className={"pp-copy-field-value pp-mono" + ((secret || (mask && !revealed)) ? " is-secret" : "")}>{shown}</code>
        {mask && (
          <button type="button" className="pp-btn pp-btn--ghost pp-btn--xs" onClick={() => setRevealed(r => !r)}
                  aria-label={revealed ? 'Hide' : 'Reveal'}>
            <EyeGlyph size={12} off={revealed}/> {revealed ? 'Hide' : 'Reveal'}
          </button>
        )}
        <button type="button" className="pp-btn pp-btn--ghost pp-btn--xs" onClick={copy}>
          {copied ? <React.Fragment><IconCheck size={12}/> Copied</React.Fragment>
                  : <React.Fragment><CopyGlyph size={12}/> Copy</React.Fragment>}
        </button>
      </div>
    </div>
  );
}

function fmtKeyDate(iso) {
  if (!iso) return '—';
  const d = new Date(iso.length === 10 ? iso + 'T00:00:00' : iso);
  if (isNaN(d.getTime())) return iso;
  return d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
}

// ── inline glyphs (not in icons.jsx) ────────────────────────────────────────
function KeyGlyph({ size = 14 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="7.5" cy="15.5" r="4.5"/><path d="M10.7 12.3 19 4M16 7l3 3M14 9l2 2"/>
    </svg>
  );
}
function CopyGlyph({ size = 12 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/>
    </svg>
  );
}
function EyeGlyph({ size = 12, off }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>
      {off && <path d="M3 3l18 18"/>}
    </svg>
  );
}
function PencilGlyph({ size = 13 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/>
    </svg>
  );
}
function RotateGlyph({ size = 13 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M21 12a9 9 0 1 1-2.6-6.4M21 4v4h-4"/>
    </svg>
  );
}
function AlertGlyph({ size = 14 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 9v4M12 17v.01"/><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/>
    </svg>
  );
}

Object.assign(window, { SettingsScreen });
