/* page-auth.jsx — Login screen + auth state machine

   Renders ABOVE the main app when no Supabase session is present.
   Once a magic link is clicked + session set, the App mounts.

   Auth round-trip rules (learned the hard way):
   - `?invite=TOKEN` arrives on the URL when a teammate clicks an invite link.
     We MUST stash it to sessionStorage on first paint, before the user clicks
     any auth button. The magic-link redirect strips query params if we don't
     either preserve them in `emailRedirectTo` OR retrieve them from session
     storage on return — we now do both, belt + suspenders.
   - `redeem_invite` runs after `onAuthStateChange` fires, so the user is
     joined into the right agency before `loadTenant` runs.
   - Owners who created an agency via `create_agency` end up with an
     `agency_members` row (role=owner, rep_id=null) but NO `reps` row, so
     `me()` returns null and /api/me falls back to demo identity. We route
     them through ProducerOnboardingWizard too — `provision_rep_for_member`
     handles owners just fine. */

(function () {

function stashInviteFromUrl() {
  try {
    const params = new URLSearchParams(window.location.search);
    const t = params.get("invite");
    if (t) {
      sessionStorage.setItem("repflow.pending_invite", t);
      return t;
    }
  } catch {}
  return sessionStorage.getItem("repflow.pending_invite") || null;
}

function LoginScreen() {
  const [email, setEmail]     = React.useState("");
  const [stage, setStage]     = React.useState("idle"); // idle | sending | sent | error
  const [errMsg, setErrMsg]   = React.useState("");
  const sb = window.getSupabase();
  const pendingInvite = stashInviteFromUrl();

  const send = async () => {
    if (!email.trim()) return;
    setStage("sending");
    try {
      // Preserve `?invite=...` across the magic-link redirect. Supabase
      // honors emailRedirectTo as long as it's listed in the project's
      // "Additional Redirect URLs" — origin+pathname+search is allowed
      // because origin+pathname is registered with `?invite=*` matching.
      const search = pendingInvite ? `?invite=${encodeURIComponent(pendingInvite)}` : "";
      const redirectTo = window.location.origin + window.location.pathname + search;

      const { error } = await sb.auth.signInWithOtp({
        email,
        options: { emailRedirectTo: redirectTo }
      });
      if (error) throw error;
      setStage("sent");
    } catch (e) {
      setErrMsg(e.message || String(e));
      setStage("error");
    }
  };

  const skip = () => {
    sessionStorage.setItem("repflow.demo", "1");
    window.dispatchEvent(new CustomEvent("auth:skip"));
  };

  return (
    <div className="login-shell">
      <div className="login-card">
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 18 }}>
          <div className="sb-brand-mark" style={{ width: 36, height: 36, fontSize: 18 }}>R</div>
          <div>
            <div style={{ fontFamily: "var(--font-display)", fontSize: 18, fontWeight: 600 }}>Repflow</div>
            <div style={{ color: "var(--text-tertiary)", fontSize: 12 }}>Operator-grade for life & health distribution</div>
          </div>
        </div>

        {pendingInvite && stage !== "sent" && (
          <div style={{ marginBottom: 12, padding: 10, background: "color-mix(in oklch, var(--accent-status) 10%, transparent)", border: "1px solid color-mix(in oklch, var(--accent-status) 30%, transparent)", borderRadius: 6, color: "var(--accent-status)", fontSize: 12, lineHeight: 1.5 }}>
            <Icons.Mail size={12}/> You're joining via an invite. Sign in with email to accept.
          </div>
        )}

        {stage === "sent" ? (
          <>
            <div style={{ padding: 14, background: "color-mix(in oklch, var(--accent-money) 10%, transparent)", border: "1px solid color-mix(in oklch, var(--accent-money) 30%, transparent)", borderRadius: 8, color: "var(--accent-money)", fontSize: 13, lineHeight: 1.5 }}>
              <Icons.Check size={14}/> Magic link sent to <strong>{email}</strong>. Click it to sign in.
            </div>
            <div style={{ marginTop: 10, fontSize: 11.5, color: "var(--text-tertiary)", lineHeight: 1.55 }}>
              Open the email on this same device so the redirect lands you back here.
              {pendingInvite && <> Your invite token is saved · we'll redeem it after sign-in.</>}
            </div>
            <button className="btn btn-ghost" style={{ marginTop: 10 }} onClick={() => { setStage("idle"); setEmail(""); }}>← Use a different email</button>
          </>
        ) : (
          <>
            <div className="field-l">Sign in with email</div>
            <input
              className="text-input"
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="you@atlasimo.com"
              onKeyDown={(e) => e.key === "Enter" && send()}
              autoFocus
              style={{ marginTop: 6, fontSize: 14, padding: "10px 12px" }}
            />
            <button
              className="btn btn-primary"
              onClick={send}
              disabled={stage === "sending" || !email.trim()}
              style={{ width: "100%", justifyContent: "center", marginTop: 10, padding: "10px 14px", fontSize: 13 }}
            >
              {stage === "sending" ? "Sending..." : <><Icons.Send size={12}/> Email me a sign-in link</>}
            </button>
            {stage === "error" && (
              <div style={{ marginTop: 10, padding: 10, background: "color-mix(in oklch, var(--state-danger) 10%, transparent)", borderRadius: 6, color: "var(--state-danger)", fontSize: 12 }}>
                {errMsg}
              </div>
            )}
            <div style={{ borderTop: "1px solid var(--border-subtle)", marginTop: 16, paddingTop: 14 }}>
              <button className="btn btn-ghost" style={{ width: "100%", justifyContent: "center" }} onClick={skip}>
                Skip → Continue with demo data
              </button>
              <div style={{ marginTop: 6, fontSize: 11, color: "var(--text-quaternary)", textAlign: "center" }}>
                No account, no real data — just the prototype on mocks.
              </div>
            </div>
          </>
        )}
      </div>
      <div className="login-foot">Atlas IMO · powered by Repflow</div>
    </div>
  );
}

function AuthGate({ children }) {
  // ?demo=1 in the URL auto-enters demo mode and persists for the session.
  if (typeof window !== "undefined" && window.location.search.indexOf("demo=1") >= 0) {
    try { sessionStorage.setItem("repflow.demo", "1"); } catch {}
  }

  // Stash any ?invite=TOKEN to sessionStorage IMMEDIATELY on mount so it
  // survives a magic-link redirect even if Supabase strips search params.
  if (typeof window !== "undefined") stashInviteFromUrl();

  const [session, setSession] = React.useState(undefined); // undefined = checking, null = no session, obj = signed in
  const [demo, setDemo]       = React.useState(sessionStorage.getItem("repflow.demo") === "1");
  const [tenant, setTenant]   = React.useState(undefined); // undefined = checking, null = no agency, obj = has agency
  const [redeeming, setRedeeming] = React.useState(false);
  const sb = window.getSupabase();

  const refreshTenant = React.useCallback(async () => {
    if (window.loadTenant) {
      const t = await window.loadTenant();
      setTenant(t);
      // Once tenant is loaded, refresh the global window.me() cache so
      // header chips, scopeRepIds, etc. immediately use the right identity.
      if (window.refreshMe) window.refreshMe();
    } else { setTenant(null); }
  }, []);

  const redeemAndRefresh = React.useCallback(async () => {
    const token = sessionStorage.getItem("repflow.pending_invite");
    if (!token) { return refreshTenant(); }
    setRedeeming(true);
    try {
      const { error } = await sb.rpc("redeem_invite", { p_token: token });
      if (error) {
        window.toast && window.toast(`Invite: ${error.message}`, "error");
      } else {
        window.toast && window.toast("Joined the agency · welcome", "success");
      }
      sessionStorage.removeItem("repflow.pending_invite");
      // Strip the param from the URL so refreshes don't re-redeem
      try {
        const url = new URL(window.location.href);
        if (url.searchParams.has("invite")) {
          url.searchParams.delete("invite");
          window.history.replaceState({}, "", url.toString());
        }
      } catch {}
    } finally {
      setRedeeming(false);
      await refreshTenant();
    }
  }, [refreshTenant, sb]);

  React.useEffect(() => {
    if (!sb) { setSession(null); return; }
    sb.auth.getSession().then(({ data }) => setSession(data.session || null));
    const { data: sub } = sb.auth.onAuthStateChange((_e, s) => {
      setSession(s);
      if (s) redeemAndRefresh();
    });
    const onSkip = () => setDemo(true);
    window.addEventListener("auth:skip", onSkip);
    return () => { sub.subscription.unsubscribe(); window.removeEventListener("auth:skip", onSkip); };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Tenant load also runs whenever session flips to truthy (covers the
  // initial getSession path which doesn't go through onAuthStateChange).
  React.useEffect(() => {
    if (session && tenant === undefined) redeemAndRefresh();
  }, [session, tenant, redeemAndRefresh]);

  if (session === undefined) {
    return <div className="login-shell"><div style={{ color: "var(--text-tertiary)", fontSize: 13 }}>Checking session...</div></div>;
  }
  if (redeeming) {
    return <div className="login-shell"><div style={{ color: "var(--text-tertiary)", fontSize: 13 }}>Joining your agency...</div></div>;
  }
  if (!session && !demo) return <LoginScreen/>;
  // Signed in but tenant lookup hasn't returned yet — avoid flashing the main
  // app (which would render with the demo/Marcus identity for a beat).
  if (session && tenant === undefined) {
    return <div className="login-shell"><div style={{ color: "var(--text-tertiary)", fontSize: 13 }}>Loading your agency...</div></div>;
  }

  // No agency_members row at all → owner setup wizard
  if (session && tenant && !tenant.member && window.OnboardingWizard) {
    const W = window.OnboardingWizard;
    return <W onComplete={() => refreshTenant()}/>;
  }
  // Member exists but no reps row yet → producer/profile wizard.
  // Owners ALSO go here because create_agency() inserts the membership row
  // but not a reps row — provision_rep_for_member fills it in (handles
  // owners + reps + managers identically).
  if (session && tenant && tenant.member && !tenant.member.rep_id && window.ProducerOnboardingWizard) {
    const P = window.ProducerOnboardingWizard;
    return <P tenant={tenant} onComplete={() => refreshTenant()}/>;
  }
  return children;
}

window.AuthGate = AuthGate;
window.LoginScreen = LoginScreen;
window.signOut = async function () {
  const sb = window.getSupabase();
  if (sb) await sb.auth.signOut();
  sessionStorage.removeItem("repflow.demo");
  sessionStorage.removeItem("repflow.pending_invite");
  // Drop the cached me() identity too so a fresh sign-in re-resolves it
  try { sessionStorage.removeItem("__repflow_me_v1"); } catch {}
  window.location.reload();
};

})();
