/* =========================================================================
 *  Python Tutor — /desktop-signin (loopback OAuth handler)
 *  -----------------------------------------------------------------------
 *  Flow (see web_builder_instructions.md §5):
 *    1. Desktop app opens the user's browser to
 *       https://python.ctwcad.com/desktop-signin?port=NNNN&state=RANDOM
 *    2. We parse `port` + `state`. `state` must be >= 16 chars (the same
 *       guard the Cloud Function enforces server-side).
 *    3. User clicks "Sign in with Google" — we run signInWithPopup; if
 *       they're already signed in we skip straight to step 4.
 *    4. Call mintPythonTutorDesktopToken({ state }). Returns
 *       { customToken, uid, email, displayName, state }.
 *    5. POST that JSON to http://127.0.0.1:${port}/cb with
 *       mode: "no-cors". The loopback server is a tiny http.server in the
 *       desktop app — no CORS handshake needed and we couldn't read the
 *       response body anyway.
 *    6. Show the "you can close this tab" confirmation. The desktop is
 *       responsible for closing the loopback socket once it has the
 *       custom token in hand.
 *
 *  We deliberately do NOT auto-trigger sign-in on mount: the user MUST
 *  click a button, otherwise browsers block the popup and silent flows
 *  feel surveillance-y. After the click we drop the user straight into
 *  the chain (no second confirmation step).
 * =======================================================================*/

const { useState, useEffect, useMemo, useCallback } = React;

// Map Firebase / callable error codes to friendly user-facing copy. The
// raw error always still goes to the console for debugging — only the
// UI message gets softened. We do this so a hiccup in Cloud Functions or
// a popup-blocker doesn't render a scary "INTERNAL" or "auth/popup-closed-
// by-user" string in the user's face.
function friendlyMessage(err) {
  if (!err) return "Something went wrong. Try again in a moment.";
  // Errors thrown by our own code (the loopback POST failure, the
  // "Cloud Function returned no custom token" guard) carry a message
  // but no Firebase code — surface them verbatim.
  if (err.message && !err.code) return err.message;
  const code = (err.code || "").toString().toLowerCase();
  if (code.endsWith("/popup-closed-by-user") || code.endsWith("/cancelled-popup-request")) {
    return "Sign-in was cancelled. Click Try again to retry.";
  }
  if (code.endsWith("/popup-blocked")) {
    return "Your browser blocked the sign-in popup. Allow popups for python.ctwcad.com and click Try again.";
  }
  if (code.endsWith("/network-request-failed")) {
    return "Network problem. Check your connection and try again.";
  }
  if (code.endsWith("/internal") || code.endsWith("/unavailable") || code.endsWith("/deadline-exceeded")) {
    return "Sign-in is temporarily unavailable. Try again in a moment, or contact Seth at sethbenricks@gmail.com if it keeps happening.";
  }
  if (code.endsWith("/unauthenticated") || code.endsWith("/permission-denied")) {
    return "Authorization failed. Sign out and try again, or contact Seth if this persists.";
  }
  if (code.endsWith("/unauthorized-domain")) {
    return "This domain isn't authorized for sign-in. Contact Seth — the site needs a Firebase Auth Authorized Domains update.";
  }
  // Last-resort: surface the message but tag the code so the user has
  // something useful to send Seth, without it being a bare "INTERNAL".
  return (err.message || "Something went wrong.") + " (debug code: " + (err.code || "unknown") + ")";
}

function parseQuery(search) {
  // Support both ?key=val (clean URL) and #/desktop-signin?key=val (hash
  // fallback used during local file:// preview). The desktop app always
  // hits the clean URL on production hosting.
  if (!search) {
    if (typeof window !== "undefined" && window.location.hash.includes("?")) {
      search = "?" + window.location.hash.split("?").slice(1).join("?");
    }
  }
  const out = {};
  if (!search) return out;
  const s = search.startsWith("?") ? search.slice(1) : search;
  s.split("&").filter(Boolean).forEach((kv) => {
    const eq = kv.indexOf("=");
    if (eq < 0) { out[decodeURIComponent(kv)] = ""; return; }
    out[decodeURIComponent(kv.slice(0, eq))] = decodeURIComponent(kv.slice(eq + 1));
  });
  return out;
}

function DesktopSignIn({ onNavigate }) {
  // Statuses: 'idle' | 'signing-in' | 'minting' | 'posting' | 'done' | 'error'
  const [status, setStatus] = useState("idle");
  const [errorMsg, setErrorMsg] = useState("");
  const [signedAs, setSignedAs] = useState(null); // { email, displayName, uid }

  // Keep a live handle to the current Firebase user, so if the page is
  // reloaded after sign-in we can skip the popup step.
  const [currentUser, setCurrentUser] = useState(null);

  const params = useMemo(() => parseQuery(window.location.search), []);
  const port = (params.port || "").toString();
  const state = (params.state || "").toString();

  const portNum = Number.parseInt(port, 10);
  const portValid = Number.isFinite(portNum) && portNum > 0 && portNum < 65536;
  const stateValid = state && state.length >= 16;
  const paramsValid = portValid && stateValid;

  useEffect(() => {
    const off = window.PYTHON_TUTOR_AUTH.onUserChange((u) => setCurrentUser(u));
    return () => { try { off && off(); } catch {} };
  }, []);

  const doFlow = useCallback(async () => {
    setErrorMsg("");
    try {
      // 1. Sign in if we don't have a user yet.
      let user = currentUser;
      if (!user) {
        setStatus("signing-in");
        user = await window.PYTHON_TUTOR_AUTH.signInWithGoogle();
        if (!user) {
          // Redirect fallback path — page is about to navigate; bail.
          return;
        }
      }

      // 2. Mint the custom token via the Cloud Function.
      setStatus("minting");
      const minted = await window.PYTHON_TUTOR_FUNCTIONS.mintPythonTutorDesktopToken({ state });
      if (!minted || !minted.customToken) {
        throw new Error("Cloud Function returned no custom token.");
      }

      // 3. POST to the loopback server. mode:'no-cors' because the
      //    127.0.0.1 server is a barebones http.server with no CORS.
      //    We can't read the response, but the fetch resolving without
      //    throwing means the bytes left the browser.
      setStatus("posting");
      try {
        await fetch(`http://127.0.0.1:${portNum}/cb`, {
          method: "POST",
          mode: "no-cors",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            customToken: minted.customToken,
            uid: minted.uid,
            email: minted.email,
            displayName: minted.displayName,
            state: minted.state,
          }),
        });
      } catch (postErr) {
        throw new Error(
          "Couldn't reach the desktop app on http://127.0.0.1:" + portNum +
          ". Make sure the Python Tutor desktop app is still open and try again."
        );
      }

      setSignedAs({
        email: minted.email,
        displayName: minted.displayName,
        uid: minted.uid,
      });
      setStatus("done");
    } catch (err) {
      // Always log the raw error for debugging — only the UI gets softened.
      console.warn("[python-tutor] desktop-signin failed:", err);
      setErrorMsg(friendlyMessage(err));
      setStatus("error");
    }
  }, [currentUser, state, portNum]);

  // ----- Invalid params: short-circuit ------------------------------------
  if (!paramsValid) {
    return (
      <div className="pt-page">
        <Topbar onNavigate={onNavigate} />
        <main className="pt-main">
          <section className="pt-soon">
            <div className="ct-eyebrow">Desktop sign-in</div>
            <h1 className="pt-soon-h1">Invalid sign-in request.</h1>
            <p className="pt-soon-sub">
              This page was opened without a valid <span className="ct-mono">port</span> and
              {" "}<span className="ct-mono">state</span> parameter. Re-launch the sign-in
              from the Python Tutor desktop app and try again.
            </p>
            <div className="pt-soon-cta">
              <button className="ct-btn ct-btn-primary ct-btn-lg" onClick={() => onNavigate("home")}>
                Back to home
              </button>
            </div>
          </section>
        </main>
        <Footer />
      </div>
    );
  }

  // ----- Status-specific copy ---------------------------------------------
  const Body = () => {
    if (status === "done") {
      return (
        <>
          <div className="ct-eyebrow" style={{ color: "var(--ct-good)" }}>Signed in</div>
          <h1 className="pt-soon-h1">You're connected.</h1>
          <p className="pt-soon-sub">
            Signed in as <strong className="ct-mono">{signedAs?.email}</strong>.
            You can close this tab and return to your desktop app.
          </p>
          <div className="pt-soon-cta">
            <button className="ct-btn ct-btn-lg" onClick={() => window.close()}>
              Close tab
            </button>
            <button className="ct-btn ct-btn-lg" onClick={() => onNavigate("home")}>
              Back to home
            </button>
          </div>
        </>
      );
    }
    if (status === "error") {
      return (
        <>
          <div className="ct-eyebrow" style={{ color: "var(--ct-danger)" }}>Sign-in failed</div>
          <h1 className="pt-soon-h1">Something went wrong.</h1>
          <p className="pt-soon-sub" style={{ color: "var(--ct-danger)" }}>
            {errorMsg || "Unknown error."}
          </p>
          <div className="pt-soon-cta">
            <button className="ct-btn ct-btn-primary ct-btn-lg" onClick={doFlow}>
              Try again
            </button>
            <button className="ct-btn ct-btn-lg" onClick={() => onNavigate("home")}>
              Back to home
            </button>
          </div>
        </>
      );
    }
    if (status === "signing-in" || status === "minting" || status === "posting") {
      const label = status === "signing-in"
        ? "Opening Google sign-in…"
        : status === "minting"
          ? "Authorizing your desktop app…"
          : "Sending credentials to the desktop app…";
      return (
        <>
          <div className="ct-eyebrow">Desktop sign-in</div>
          <h1 className="pt-soon-h1">Signing in…</h1>
          <p className="pt-soon-sub">
            <Spinner /> {label}
          </p>
        </>
      );
    }
    // idle
    return (
      <>
        <div className="ct-eyebrow">Desktop sign-in</div>
        <h1 className="pt-soon-h1">Authorize the desktop app.</h1>
        <p className="pt-soon-sub">
          Sign in with Google to authorize your Python Tutor desktop app
          {" "}({currentUser ? <strong className="ct-mono">{currentUser.email}</strong> : "no account selected"}).
          The desktop app is listening on
          {" "}<span className="ct-mono">127.0.0.1:{portNum}</span> and will pick up the credentials automatically.
        </p>
        <div className="pt-soon-cta">
          <button className="ct-btn ct-btn-primary ct-btn-lg" onClick={doFlow}>
            {currentUser ? "Authorize this account" : "Sign in with Google"}
          </button>
          {currentUser && (
            <button
              className="ct-btn ct-btn-lg"
              onClick={async () => { await window.PYTHON_TUTOR_AUTH.signOut(); }}
            >
              Use a different account
            </button>
          )}
        </div>
      </>
    );
  };

  return (
    <div className="pt-page">
      <Topbar onNavigate={onNavigate} />
      <main className="pt-main">
        <section className="pt-soon">
          <Body />
        </section>
      </main>
      <Footer />
    </div>
  );
}

// ----- Tiny shared bits (kept inline so this file is self-contained) ------
function Topbar({ onNavigate }) {
  // PyBrand from brandmark.jsx renders the new ">_" mark + word; we
  // wrap it in a button so it stays clickable as the home affordance.
  return (
    <header className="pt-topbar">
      <button className="pt-brand-btn" onClick={() => onNavigate("home")}>
        <window.PyBrand size={20} />
      </button>
    </header>
  );
}

function Footer() {
  return (
    <footer className="pt-footer">
      <div className="ct-mono ct-dim pt-footer-line">
        Part of the CTWCAD family. Built by Seth Ricks.
      </div>
    </footer>
  );
}

function Spinner() {
  // Pure-CSS spinner so we don't pull in an extra icon. Lives inline so
  // the dependency is just one tiny <span>.
  return (
    <span
      aria-hidden="true"
      style={{
        display: "inline-block",
        width: 14, height: 14,
        marginRight: 8,
        verticalAlign: "-2px",
        border: "2px solid var(--ct-border)",
        borderTopColor: "var(--ct-accent)",
        borderRadius: "50%",
        animation: "pt-spin 0.8s linear infinite",
      }}
    />
  );
}

window.PythonTutorDesktopSignIn = DesktopSignIn;
