import { useEffect, useState } from "react";
import { api, BASE_URL, NetworkError } from "./api/client";
import { showError } from "./banner";
import { BannerStack } from "./components/BannerStack";
import "./LoginGate.css";
import App from "./App";
import { OnboardingWizard } from "./OnboardingWizard";
import type { SessionInfo } from "./types/api";

type Status = "loading" | "authenticated" | "unauthenticated";

const AUTH_ERROR_MESSAGES: Record<string, string> = {
  forbidden: "Your account isn't authorized for geniusrun.",
  failed: "Login failed, please try again.",
};

// Wraps App: on mount, asks the backend whether this browser already has a
// valid session (GET /api/session/me). geniusrun has no anonymous view, so
// this is the first fork -- "show the login screen" vs "show the app." A
// second fork, once authenticated, is whether the session's account has a
// provisioned profile yet (session.has_profile) -- a brand-new OIDC login
// sees OnboardingWizard instead of App until it completes one. Nothing is
// persisted until the wizard's Garmin-connect step actually succeeds (see
// docs/superpowers/specs/2026-07-26-onboarding-wizard-deferred-commit-design.md),
// so has_profile alone is always sufficient here -- there's no separate
// "provisioned but not connected" state to gate on.
export function LoginGate() {
  const [status, setStatus] = useState<Status>("loading");
  const [session, setSession] = useState<SessionInfo | null>(null);

  useEffect(() => {
    api
      .getSessionInfo()
      .then((s) => {
        setSession(s);
        setStatus("authenticated");
      })
      .catch((e) => {
        // A NetworkError here means the backend itself isn't reachable, not
        // that this browser is genuinely logged out -- worth a banner, since
        // otherwise this is the one place in the app where "backend not
        // here" would show no feedback at all. Either way the screen still
        // falls through to "unauthenticated" (showing the Log in button),
        // since there's no session to trust regardless of why the check
        // failed.
        if (e instanceof NetworkError) showError(e.message);
        setStatus("unauthenticated");
      });
  }, []);

  // Keycloak redirects back here with ?auth_error=<code> when the
  // login-gate's own role check (not Keycloak's own authentication) rejects
  // a session -- surfaced once, as a banner, the moment this screen is
  // reached.
  useEffect(() => {
    if (status !== "unauthenticated") return;
    const authError = new URLSearchParams(window.location.search).get("auth_error");
    if (authError) showError(AUTH_ERROR_MESSAGES[authError] ?? "Login failed, please try again.");
  }, [status]);

  if (status === "loading") {
    return (
      <>
        <BannerStack />
        <div className="login-gate-loading">Loading…</div>
      </>
    );
  }

  if (status === "unauthenticated") {
    return (
      <>
        <BannerStack />
        <div className="login-gate">
          <h1>🧞‍♀️ geniusrun</h1>
          <a className="login-gate-button" href={`${BASE_URL}/api/session/login`}>
            Log in
          </a>
        </div>
      </>
    );
  }

  if (!session!.has_profile) {
    return (
      <OnboardingWizard
        onCreated={(displayName) => setSession((s) => (s ? { ...s, has_profile: true, display_name: displayName } : s))}
      />
    );
  }

  return <App session={session!} />;
}
