// site-auth.jsx — Firebase Auth bootstrap + AuthGate + helpers.
//
// F018 Phase 1 (F018T00). Single source of truth for "is the user
// signed in," "send them a magic link," "complete a magic-link sign-in
// after they clicked the email," "sign them out."
//
// Loaded via index.html as a Babel-standalone <script>. No bundler.
// Exposes: window.exoteamAuth = { useAuth, AuthGate, sendMagicLink,
// completeMagicLinkSignIn, signOut, firebaseApp, db, fns, auth }.
//
// Decisions (locked in F018):
// - Q1: magic-link only at v1. Google + Apple Sign-In deferred.
// - Q4: display_name is REQUIRED at sign-up; never derived from email.
// - Q7: magic-link sender = noreply@exoteam.ai (configured in Firebase
//   Console's Auth → Templates pane — F018T32).
//
// Firebase project: `exoteam` (Firebase project ID, on the exoteam.ai
// Cloud Org post-R02 M1 migration).

(function (window) {
  'use strict';

  // ---------- Firebase config ----------
  //
  // The Web SDK config is public by design — Firebase secures access
  // via Firestore rules + Auth domain whitelist, NOT via the apiKey.
  // Safe to commit. Get these values from the Firebase Console →
  // Project settings → General → Your apps → Web app.
  //
  // TODO(operator, M3 runbook §1+§2): paste the real values below.
  // While the placeholders are in place, all auth calls will fail
  // loudly — useful during local development to surface forgotten
  // config.
  const firebaseConfig = {
    apiKey: "AIzaSyCXxj3fJqKqJgL_ypisPF-yonwO5jfFIHY",
    authDomain: "exoteam.firebaseapp.com",
    projectId: "exoteam",
    storageBucket: "exoteam.firebasestorage.app",
    messagingSenderId: "836466649285",
    appId: "1:836466649285:web:a6cc5545446bd2fc1028ac"
  };

  // ---------- Firebase init ----------
  // The compat builds are loaded via <script> in index.html, which
  // exposes the `firebase` global. Initialise idempotently.
  if (typeof firebase === 'undefined') {
    console.error(
      'site-auth.jsx: firebase global not found. ' +
        'Check that the Firebase SDK <script> tags in index.html load before this file.'
    );
    return;
  }
  if (!firebase.apps || firebase.apps.length === 0) {
    firebase.initializeApp(firebaseConfig);
  }
  const firebaseApp = firebase.app();
  const auth = firebase.auth();
  const db = firebase.firestore();
  // Functions run in europe-west2 (London — closest to UK + matches the
  // eur3 Firestore region). Compat SDK syntax: app.functions(region).
  // Without specifying region the client defaults to us-central1 and the
  // httpsCallable POST hits the wrong URL → CORS preflight fails.
  const fns = firebaseApp.functions
    ? firebaseApp.functions('europe-west2')
    : firebase.functions
    ? firebase.functions()
    : null;

  // ---------- Magic-link config ----------
  //
  // The link in the email points back at the website with
  // ?finishSignIn=1 so we know to complete the sign-in on landing.
  function magicLinkActionCodeSettings(emailForFollowup) {
    const continueURL = new URL(window.location.origin + '/onboard/signup');
    continueURL.searchParams.set('finishSignIn', '1');
    // Some email apps strip URL params; we cache the email locally too.
    if (emailForFollowup) {
      window.localStorage.setItem('exoteam_magic_link_email', emailForFollowup);
    }
    return {
      url: continueURL.toString(),
      handleCodeInApp: true,
    };
  }

  async function sendMagicLink(email) {
    // F018 custom magic-link path — calls the Cloud Function which:
    //   1. Generates the action link with Admin SDK
    //   2. Sends a custom-designed HTML email via Resend
    //      from "Exoteam <noreply@exoteam.ai>" (not the
    //      Firebase-default firebaseapp.com template).
    //
    // Cache the email locally so the magic-link landing can resume
    // sign-in without re-prompting for the address (Firebase requires
    // the email for signInWithEmailLink to mitigate session hijacking).
    if (!fns) {
      throw new Error('Firebase Functions SDK not loaded');
    }
    window.localStorage.setItem('exoteam_magic_link_email', email);
    const settings = magicLinkActionCodeSettings(email);
    const callable = fns.httpsCallable('sendMagicLink');
    await callable({ email, continueUrl: settings.url });
  }

  function isMagicLinkLanding() {
    return auth.isSignInWithEmailLink(window.location.href);
  }

  async function completeMagicLinkSignIn() {
    // If the email was cached on the same browser, use it. Otherwise the
    // caller (the SignInForm) prompts the user for their email so we
    // can verify the link.
    let email = window.localStorage.getItem('exoteam_magic_link_email');
    if (!email) {
      email = window.prompt('Confirm your email for sign-in:');
      if (!email) throw new Error('email-required-to-complete-signin');
    }
    const result = await auth.signInWithEmailLink(email, window.location.href);
    window.localStorage.removeItem('exoteam_magic_link_email');
    return result;
  }

  function signOut() {
    return auth.signOut();
  }

  // ---------- useAuth hook ----------
  //
  // Subscribes to Firebase Auth state, returns
  // { user, loading, signOut, userDoc, refreshUserDoc }.
  // `userDoc` is the Firestore /users/{uid} doc snapshot (or null).
  // Also handles the first-sign-in bootstrap: writes a stub users/{uid}
  // doc which triggers the onUserCreate Cloud Function to create the
  // workspace + membership + onboarding atomically.
  function useAuth() {
    const [user, setUser] = React.useState(null);
    const [loading, setLoading] = React.useState(true);
    const [userDoc, setUserDoc] = React.useState(null);

    React.useEffect(() => {
      const unsubAuth = auth.onAuthStateChanged((u) => {
        setUser(u);
        setLoading(false);
      });
      return () => unsubAuth();
    }, []);

    React.useEffect(() => {
      if (!user) {
        setUserDoc(null);
        return undefined;
      }
      const ref = db.collection('users').doc(user.uid);

      // Bootstrap is a ONE-SHOT get() — using onSnapshot for this would
      // sometimes fire with a stale-cache emit (snap.exists=false even
      // when the doc exists), triggering a redundant set() that Firestore
      // evaluates as UPDATE (rule rejects it because it changes more
      // fields than the update rule allows).
      let cancelled = false;
      ref
        .get({ source: 'server' })
        .then(async (snap) => {
          if (cancelled || snap.exists) return;
          try {
            await ref.set({
              email: user.email || null,
              display_name: null,
              providers: ['password'],
              created_at: firebase.firestore.FieldValue.serverTimestamp(),
              locale: typeof navigator !== 'undefined' ? navigator.language : 'en',
              tz: typeof Intl !== 'undefined'
                ? Intl.DateTimeFormat().resolvedOptions().timeZone
                : 'UTC',
            });
          } catch (err) {
            console.error('useAuth: bootstrap users doc failed', err);
          }
        })
        .catch((err) =>
          console.error('useAuth: bootstrap get failed', err)
        );

      // Live listener — just mirrors whatever's in Firestore.
      const unsubDoc = ref.onSnapshot(
        (snap) =>
          setUserDoc(snap.exists ? { id: snap.id, ...snap.data() } : null),
        (err) => console.error('useAuth: users doc snapshot error', err)
      );
      return () => {
        cancelled = true;
        unsubDoc();
      };
    }, [user]);

    return {
      user,
      loading,
      userDoc,
      signOut,
    };
  }

  // ---------- AuthGate ----------
  //
  // Wraps protected content. If not signed in, renders the SignInForm
  // (magic-link only per Q1). If signed in, renders children.
  // Loading shows a centred spinner so the page doesn't flash.
  function AuthGate({ children }) {
    const { user, loading } = useAuth();

    // If the URL is the magic-link landing, complete sign-in first.
    const [linkCompleting, setLinkCompleting] = React.useState(false);
    const [linkError, setLinkError] = React.useState(null);
    React.useEffect(() => {
      if (!loading && !user && isMagicLinkLanding()) {
        setLinkCompleting(true);
        completeMagicLinkSignIn()
          .catch((err) => {
            console.error('AuthGate: magic-link sign-in failed', err);
            setLinkError(err.message || String(err));
          })
          .finally(() => setLinkCompleting(false));
      }
    }, [loading, user]);

    if (loading || linkCompleting) return <CentredSpinner label="Signing you in…" />;
    if (linkError) return <SignInForm initialError={linkError} />;
    if (!user) return <SignInForm />;
    return children;
  }

  // ---------- SignInForm ----------
  function SignInForm({ initialError }) {
    const [email, setEmail] = React.useState('');
    const [submitting, setSubmitting] = React.useState(false);
    const [linkSent, setLinkSent] = React.useState(false);
    const [error, setError] = React.useState(initialError || null);

    async function handleSubmit(e) {
      e.preventDefault();
      if (!email.trim()) return;
      setSubmitting(true);
      setError(null);
      try {
        await sendMagicLink(email.trim());
        setLinkSent(true);
      } catch (err) {
        console.error('SignInForm: sendMagicLink failed', err);
        setError(
          err && err.message
            ? `Couldn't send the link: ${err.message}`
            : "Couldn't send the link — please try again."
        );
      } finally {
        setSubmitting(false);
      }
    }

    if (linkSent) {
      return (
        <div className="exo-auth-shell">
          <div className="exo-auth-card">
            <h1 className="exo-auth-title">Check your inbox</h1>
            <p className="exo-auth-body">
              We sent a sign-in link to <strong>{email}</strong>. Open it on
              this device to continue.
            </p>
            <p className="exo-auth-hint">
              No email? Check spam, or{' '}
              <button
                type="button"
                className="exo-link"
                onClick={() => setLinkSent(false)}
              >
                try a different address
              </button>
              .
            </p>
          </div>
        </div>
      );
    }

    return (
      <div className="exo-auth-shell">
        <form className="exo-auth-card" onSubmit={handleSubmit}>
          <h1 className="exo-auth-title">Sign in to exoteam</h1>
          <p className="exo-auth-body">
            Your assistant for sales, fundraising, and grants.
            <br />
            14-day trial. No card required.
          </p>
          <label className="exo-auth-label" htmlFor="exo-auth-email">
            Email address
          </label>
          <input
            id="exo-auth-email"
            type="email"
            required
            autoFocus
            autoComplete="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            className="exo-auth-input"
            placeholder="you@yourcompany.com"
            disabled={submitting}
          />
          {error && <p className="exo-auth-error">{error}</p>}
          <button
            type="submit"
            className="exo-auth-submit"
            disabled={submitting || !email.trim()}
          >
            {submitting ? 'Sending…' : 'Continue with email'}
          </button>
          <p className="exo-auth-tos">
            By continuing you agree to our{' '}
            <a href="/terms">Terms</a> and <a href="/privacy">Privacy Policy</a>.
          </p>
        </form>
      </div>
    );
  }

  function CentredSpinner({ label }) {
    return (
      <div className="exo-auth-shell">
        <div className="exo-auth-card exo-auth-card--centred">
          <div className="exo-spinner" aria-hidden="true" />
          {label && <p className="exo-auth-body">{label}</p>}
        </div>
      </div>
    );
  }

  // ---------- Workspace URL helpers (R03 / T0007) ----------
  //
  // Every signed-in surface lives under `/w/<workspaceId>/...`. These
  // helpers parse / build / navigate those URLs so the dashboard,
  // settings, and switcher stay in lockstep.

  function parseWorkspaceIdFromPath(pathname) {
    const m = (pathname || window.location.pathname).match(/^\/w\/([^/]+)(?:\/|$)/);
    return m ? m[1] : null;
  }

  function workspacePath(workspaceId, subPath) {
    if (!workspaceId) return subPath || '/';
    const tail = (subPath || '').replace(/^\/+/, '');
    return tail ? `/w/${workspaceId}/${tail}` : `/w/${workspaceId}/dashboard`;
  }

  function navigateTo(path) {
    if (window.location.pathname === path) return;
    window.history.pushState({}, '', path);
    window.dispatchEvent(new PopStateEvent('popstate'));
  }

  // Read-only React hook: which workspace id is in the URL right now?
  // Updates on popstate so workspace-switcher clicks propagate. Returns
  // null if the URL has no /w/<id>/ prefix.
  function useActiveWorkspaceId() {
    const [wid, setWid] = React.useState(() => parseWorkspaceIdFromPath());
    React.useEffect(() => {
      const onPop = () => setWid(parseWorkspaceIdFromPath());
      window.addEventListener('popstate', onPop);
      return () => window.removeEventListener('popstate', onPop);
    }, []);
    return wid;
  }

  // Returns { workspaces, loading, error, activeWorkspaceId, activeWorkspace }.
  // Powers the Workspace switcher + serves as the resolver for "which
  // workspace is the user looking at right now."
  //
  // Fallback rule (per task spec ## Context / rationale): if no /w/<id>/
  // prefix is in the URL, fall back to the first workspace from
  // listMyWorkspaces so a bare /dashboard email link still lands
  // somewhere reasonable. Top-level router redirects to the prefixed
  // form once the workspace id is known.
  function useWorkspace() {
    const { user } = useAuth();
    const activeWorkspaceId = useActiveWorkspaceId();
    const [workspaces, setWorkspaces] = React.useState([]);
    const [loading, setLoading] = React.useState(true);
    const [error, setError] = React.useState(null);

    React.useEffect(() => {
      if (!user || !fns) {
        setWorkspaces([]);
        setLoading(false);
        return undefined;
      }
      let cancelled = false;
      setLoading(true);
      setError(null);
      const callable = fns.httpsCallable('listMyWorkspaces');
      callable({})
        .then(({ data }) => {
          if (cancelled) return;
          setWorkspaces((data && data.workspaces) || []);
          setLoading(false);
        })
        .catch((err) => {
          if (cancelled) return;
          console.error('useWorkspace: listMyWorkspaces failed', err);
          setError(err.message || String(err));
          setLoading(false);
        });
      return () => {
        cancelled = true;
      };
    }, [user]);

    const resolvedId =
      activeWorkspaceId ||
      (workspaces.length > 0 ? workspaces[0].workspace_id : null);
    const activeWorkspace =
      workspaces.find((w) => w.workspace_id === resolvedId) || null;

    return {
      workspaces,
      loading,
      error,
      activeWorkspaceId: resolvedId,
      activeWorkspace,
    };
  }

  // ---------- expose ----------
  window.exoteamAuth = {
    firebaseApp,
    auth,
    db,
    fns,
    useAuth,
    AuthGate,
    SignInForm,
    sendMagicLink,
    completeMagicLinkSignIn,
    signOut,
    isMagicLinkLanding,
    // Workspace helpers (R03 / T0007)
    parseWorkspaceIdFromPath,
    workspacePath,
    navigateTo,
    useActiveWorkspaceId,
    useWorkspace,
  };
})(window);
