// Push notification subscription flow.
//
// Flow:
//   1. Component mounts, checks /api/push/config to see if push is server-
//      configured (VAPID keys set). If not, hides itself entirely.
//   2. Checks Notification.permission state ('default' / 'granted' / 'denied').
//   3. If 'default', renders an "Enable notifications" pill in the topbar.
//   4. On click → Notification.requestPermission() → if granted, calls
//      navigator.serviceWorker.ready → registration.pushManager.subscribe().
//   5. POSTs the subscription to /api/push/subscribe so the server can
//      dispatch to this device on autonomy events.
//
// Renders a tiny status pill that shows the current state: enabled,
// muted, or "click to enable". Click toggles. Can also fire a test push
// from the pill's hover/dropdown menu.
//
// Works on Chrome / Edge / Firefox / Safari 16.4+ on macOS / iOS PWA
// installs (Add to Home Screen). Also works inside the Capacitor-wrapped
// iOS / Android app via the native push plugin (which goes through
// APNs / FCM but appears to JS as the same PushManager API).

const { useState: pUseState, useEffect: pUseEffect, useCallback: pUseCallback } = React;

// Convert URL-safe base64 (VAPID public key) into the Uint8Array that
// PushManager.subscribe() requires. Standard helper from the Web Push docs.
function urlB64ToUint8Array(b64) {
  const padding = '='.repeat((4 - b64.length % 4) % 4);
  const padded = (b64 + padding).replace(/-/g, '+').replace(/_/g, '/');
  const raw = atob(padded);
  const out = new Uint8Array(raw.length);
  for (let i = 0; i < raw.length; i++) out[i] = raw.charCodeAt(i);
  return out;
}

function PushControl() {
  const [supported, setSupported] = pUseState(false);
  const [serverConfigured, setServerConfigured] = pUseState(false);
  const [publicKey, setPublicKey] = pUseState(null);
  const [permission, setPermission] = pUseState('default');
  const [subscribed, setSubscribed] = pUseState(false);
  const [busy, setBusy] = pUseState(false);
  const [msg, setMsg] = pUseState('');

  // Initial probe — runs once on mount
  pUseEffect(() => {
    const sw = 'serviceWorker' in navigator;
    const pn = 'PushManager' in window;
    const nf = 'Notification' in window;
    if (!(sw && pn && nf)) {
      setSupported(false);
      return;
    }
    setSupported(true);
    setPermission(Notification.permission);

    (async () => {
      try {
        // Server config
        const r = await fetch('/api/push/config');
        if (r.ok) {
          const j = await r.json();
          setServerConfigured(Boolean(j.configured));
          setPublicKey(j.public_key || null);
        }
      } catch {}
      try {
        // Existing subscription on this device
        const reg = await navigator.serviceWorker.getRegistration();
        if (reg) {
          const sub = await reg.pushManager.getSubscription();
          setSubscribed(Boolean(sub));
        }
      } catch {}
    })();
  }, []);

  const subscribe = pUseCallback(async () => {
    if (busy) return;
    setBusy(true);
    setMsg('');
    try {
      // Permission first — Notification.requestPermission MUST be called
      // from a user-gesture handler (click), so we keep this flow inside
      // the button's onClick.
      let perm = Notification.permission;
      if (perm === 'default') {
        perm = await Notification.requestPermission();
      }
      setPermission(perm);
      if (perm !== 'granted') {
        setMsg('Permission denied. You can enable in browser settings.');
        return;
      }

      const reg = await navigator.serviceWorker.ready;
      let sub = await reg.pushManager.getSubscription();
      if (!sub) {
        if (!publicKey) {
          throw new Error('Server has no VAPID public key configured.');
        }
        sub = await reg.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: urlB64ToUint8Array(publicKey),
        });
      }
      // POST to backend
      const subJson = sub.toJSON();
      const r = await fetch('/api/push/subscribe', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          endpoint: subJson.endpoint,
          keys: subJson.keys,
        }),
      });
      if (!r.ok) {
        const t = await r.text();
        throw new Error(`HTTP ${r.status}: ${t.slice(0, 80)}`);
      }
      setSubscribed(true);
      setMsg('subscribed · phone will buzz on autonomy fires');
      setTimeout(() => setMsg(''), 4000);
    } catch (e) {
      setMsg(`failed: ${e.message || e}`);
    } finally {
      setBusy(false);
    }
  }, [busy, publicKey]);

  const unsubscribe = pUseCallback(async () => {
    if (busy) return;
    setBusy(true);
    setMsg('');
    try {
      const reg = await navigator.serviceWorker.ready;
      const sub = await reg.pushManager.getSubscription();
      if (sub) {
        const ep = sub.endpoint;
        await sub.unsubscribe();
        await fetch('/api/push/subscribe', {
          method: 'DELETE',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ endpoint: ep }),
        }).catch(() => {});
      }
      setSubscribed(false);
      setMsg('unsubscribed');
      setTimeout(() => setMsg(''), 3000);
    } catch (e) {
      setMsg(`failed: ${e.message || e}`);
    } finally {
      setBusy(false);
    }
  }, [busy]);

  const test = pUseCallback(async () => {
    try {
      const r = await fetch('/api/push/test', { method: 'POST' });
      const j = await r.json().catch(() => ({}));
      setMsg(j.sent ? `test sent · ${j.sent} device${j.sent === 1 ? '' : 's'}` : 'no devices subscribed');
      setTimeout(() => setMsg(''), 4000);
    } catch (e) {
      setMsg(`test failed: ${e.message || e}`);
    }
  }, []);

  // If browser doesn't support push or the server isn't configured,
  // hide entirely — don't tease a feature that doesn't work.
  if (!supported || !serverConfigured) return null;

  // Render: small pill in the topbar. State-dependent styling.
  const denied = permission === 'denied';
  const enabled = subscribed && permission === 'granted';
  const color = denied ? '#d96a6a' : enabled ? '#5fb37c' : 'var(--text-2)';
  const bg = enabled ? 'rgba(95,179,124,0.10)' : 'transparent';
  const label = denied ? '🔕 blocked' : enabled ? '🔔 on' : '🔔 enable';
  const onClick = denied ? null : (enabled ? unsubscribe : subscribe);
  const title = denied
    ? 'Notifications blocked. Enable in browser/system settings, then refresh.'
    : enabled
      ? `Tibeb pushes a banner when an event/alert/twitter memo lands. Click to disable. Shift+click to send a test.`
      : 'Get a push notification when an autonomy memo lands. Click to enable.';

  return (
    <span style={{ position: 'relative', display: 'inline-block' }}>
      <button
        className="agents-btn"
        onClick={(e) => {
          if (e.shiftKey && enabled) test();
          else if (onClick) onClick();
        }}
        disabled={busy}
        title={title}
        style={{
          color, background: bg,
          borderColor: enabled ? 'rgba(95,179,124,0.35)' : 'var(--line)',
          fontWeight: enabled ? 600 : 400,
          opacity: busy ? 0.5 : 1,
        }}
      >
        {label}
      </button>
      {msg && (
        <span style={{
          position: 'absolute', top: 'calc(100% + 4px)', right: 0,
          padding: '4px 8px', fontSize: 9,
          fontFamily: "'JetBrains Mono', monospace",
          background: msg.startsWith('failed') ? 'rgba(217,106,106,0.18)' : 'rgba(95,179,124,0.18)',
          color: msg.startsWith('failed') ? '#d96a6a' : '#5fb37c',
          border: '1px solid var(--line)',
          borderRadius: 3,
          whiteSpace: 'nowrap',
          zIndex: 50,
          pointerEvents: 'none',
        }}>{msg}</span>
      )}
    </span>
  );
}

window.PushControl = PushControl;
