/* Shared sign-in gate for the Signal pipeline, Telegram feed, and My account
 * pages. Component names (GoogleSignInGate, InlineSignIn) are kept as-is even
 * though the identity provider moved on 2026-10-02 — Google Identity Services
 * -> Clerk — since they're consumed from widgets-account.jsx, widgets-adhoc.jsx,
 * widgets-signals.jsx and widgets-telegram.jsx and a rename would touch all four
 * for no functional reason.
 *
 * Clerk owns the actual sign-in UI and session cookie entirely (clerk-js,
 * loaded via script tag in index.html — no bundler, same loading style as
 * everything else here). The server verifies Clerk's session and only then
 * treats a request as authenticated — see src/auth.js — same real protection
 * the Google version had, just a different provider doing the verifying.
 */

// Resolves once clerk-js has finished loading (window.Clerk.load()). Needed
// because the script tag is async — mirrors the old polling-for-window.google
// pattern for the same reason.
function useClerkReady() {
  const [ready, setReady] = React.useState(!!(window.Clerk && window.Clerk.loaded));

  React.useEffect(() => {
    if (ready) return undefined;
    let cancelled = false;

    async function tryLoad() {
      if (cancelled || !window.Clerk) return false;
      try {
        await window.Clerk.load();
      } catch (_) { /* surfaced to the caller as "never becomes ready" */ }
      if (!cancelled) setReady(true);
      return true;
    }

    if (!tryLoad()) {
      const id = setInterval(() => { if (window.Clerk) { clearInterval(id); tryLoad(); } }, 150);
      const timeout = setTimeout(() => clearInterval(id), 10000);
      return () => { cancelled = true; clearInterval(id); clearTimeout(timeout); };
    }
    return () => { cancelled = true; };
  }, [ready]);

  return ready;
}

// Mounts Clerk's own sign-in widget into the returned ref once Clerk is ready.
// Shared by GoogleSignInGate (blocks a whole page) and InlineSignIn (gates one
// action while the rest of the page stays open) — same Clerk session either
// way. `active` controls whether this mounts at all, so a caller that only
// wants it while signed-out can pass that condition straight through.
function useClerkSignIn(active, onSignedIn) {
  const [error, setError] = React.useState(null);
  const elRef = React.useRef(null);
  const ready = useClerkReady();

  React.useEffect(() => {
    if (!active || !ready || !elRef.current) return undefined;
    const el = elRef.current;
    setError(null);
    try {
      window.Clerk.mountSignIn(el);
    } catch (e) {
      setError(String(e.message || e));
      return undefined;
    }
    // Fires on any session change while mounted — a fresh sign-in here, or one
    // that happened in another tab. The caller's onSignedIn flips local status;
    // the 'auth-changed' event is what the rest of the app (App's own session
    // state) listens for.
    const unsubscribe = window.Clerk.addListener(({ session }) => {
      if (session) {
        window.dispatchEvent(new Event('auth-changed'));
        onSignedIn && onSignedIn();
      }
    });
    return () => {
      try { window.Clerk.unmountSignIn(el); } catch (_) { /* already gone */ }
      unsubscribe();
    };
  }, [active, ready]);

  return { elRef, error };
}

function GoogleSignInGate({ title, subtitle, children }) {
  const [status, setStatus] = React.useState('checking'); // checking | signed-out | signed-in

  React.useEffect(() => {
    fetch('/api/auth/me', { credentials: 'include' }).then(r => r.json())
      .then(j => setStatus(j.ok && j.user ? 'signed-in' : 'signed-out'))
      .catch(() => setStatus('signed-out'));
  }, []);

  const { elRef, error } = useClerkSignIn(status === 'signed-out', () => setStatus('signed-in'));

  if (status === 'checking') {
    return <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-4)', fontSize: 12 }}>Loading…</div>;
  }
  if (status === 'signed-out') {
    return (
      <div className="card" style={{ gridColumn: 'span 12', maxWidth: 400, margin: '48px auto' }}>
        <div className="card-b" style={{ textAlign: 'center', padding: '32px 26px' }}>
          <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 6 }}>🔒 {title || 'Protected page'}</div>
          <div style={{ fontSize: 12, color: 'var(--text-3)', marginBottom: 20 }}>
            {subtitle || 'Sign in to continue.'}
          </div>
          <div ref={elRef} style={{ display: 'flex', justifyContent: 'center', minHeight: 40 }} />
          {error && <div style={{ marginTop: 12, fontSize: 11, color: 'var(--bear)' }}>{error}</div>}
        </div>
      </div>
    );
  }
  return children;
}

// Compact sign-in prompt for gating a single action (a button, a form) rather
// than a whole page — everything around it stays visible/usable. `onSignedIn`
// fires once a session exists; the caller's own `user` prop (from App's
// session state, refreshed via the 'auth-changed' event this already
// dispatches) is what should actually flip the gated action back on.
function InlineSignIn({ label }) {
  const { elRef, error } = useClerkSignIn(true, null);
  return (
    <div style={{ display: 'inline-flex', flexDirection: 'column', gap: 5 }}>
      {label && <div style={{ fontSize: 11, color: 'var(--text-3)' }}>{label}</div>}
      <div ref={elRef} style={{ minHeight: 40 }} />
      {error && <div style={{ fontSize: 10.5, color: 'var(--bear)' }}>{error}</div>}
    </div>
  );
}
