/* =========================================================================
 *  CTWCAD — Connect view (desktop-app device link)
 *  -----------------------------------------------------------------------
 *  Reached via https://ctwcad.com/?connect=1&port=NNNN&state=XXX&name=Device
 *  The desktop CTWCAD app starts a local loopback server, opens the user's
 *  default browser to that URL, and waits for a POST to
 *  http://localhost:NNNN/callback containing the user's Firebase ID and
 *  refresh tokens. The desktop app DPAPI-encrypts those and uses them as
 *  its long-lived credential.
 *
 *  Why refresh-token instead of a server-validated API key: the site is
 *  static (no backend) and Firestore rules only authenticate Firebase
 *  ID tokens. The desktop app exchanges its refresh token via the public
 *  securetoken.googleapis.com endpoint to get fresh ID tokens, exactly
 *  the same way the web SDK does internally. We track the connection
 *  with a /users/{uid}/apiKeys/{id} doc so admins can revoke devices.
 * =======================================================================*/

function ConnectView({ user, signedIn, onSignIn }) {
  const M = window.FramerMotion.motion;
  const [params] = useState(() => {
    const sp = new URLSearchParams(window.location.search);
    return {
      port: parseInt(sp.get("port") || "0", 10),
      state: sp.get("state") || "",
      name: (sp.get("name") || "CTWCAD Desktop").slice(0, 64),
    };
  });
  const [status, setStatus] = useState("idle"); // idle | authing | ok | err | denied
  const [error, setError] = useState(null);

  const paramsValid = !!params.port && params.port > 1024 && params.port < 65536 && !!params.state;

  // Block obvious nonsense before doing anything.
  useEffect(() => {
    if (!paramsValid) setStatus("err");
  }, [paramsValid]);

  // Live-recheck the allowlist once signed in (the localStorage cache may be stale).
  const [allowedChecked, setAllowedChecked] = useState(null);
  useEffect(() => {
    if (!signedIn || !user?.email) return;
    let alive = true;
    (async () => {
      try {
        const cfg = await window.api?.getAccessConfig?.();
        const e = user.email.toLowerCase();
        const ok = !!cfg?.public
          || (cfg?.allowedEmails || []).includes(e)
          || (cfg?.allowlist || []).some(x => x.email.toLowerCase() === e)
          || window.CTWCAD_ACCESS?.isOwner(user.email);
        if (alive) setAllowedChecked(!!ok);
      } catch {
        // Fall back to the synchronous cache if the live check fails.
        if (alive) setAllowedChecked(!!window.CTWCAD_ACCESS?.isAllowed(user.email));
      }
    })();
    return () => { alive = false; };
  }, [signedIn, user?.email]);

  const handleAuthorize = async () => {
    if (!paramsValid) { setStatus("err"); setError("Bad request — missing port/state."); return; }
    if (!window.firebaseAuth?.currentUser) {
      setStatus("err"); setError("Not signed in to Firebase. Refresh and try again.");
      return;
    }
    setStatus("authing");
    setError(null);
    try {
      // 1) Track the connection in Firestore so admins can see / revoke it.
      const key = await window.api.createApiKey({
        name: "CTWCAD Desktop",
        deviceName: params.name,
        os: "windows",
      });
      // 2) Pull current Firebase credentials. The refresh token is what the
      //    desktop app actually keeps — ID tokens expire after 1h.
      const fbUser = window.firebaseAuth.currentUser;
      const idToken = await fbUser.getIdToken(/*forceRefresh*/ false);
      const refreshToken = fbUser.refreshToken;
      // 3) POST to the desktop loopback server.
      //    - Use 127.0.0.1, not localhost: on Windows w/ IPv6 enabled,
      //      localhost may resolve to ::1 first and the Python loopback
      //      binds 127.0.0.1, so the POST silently fails to reach it.
      //    - Use mode: "cors" (not "no-cors"). With no-cors fetch
      //      returns successfully even when the POST never landed,
      //      which made the page show "Connected ✓" while the desktop
      //      app got nothing. With cors we can read res.ok and the
      //      catch branch fires on real failures.
      //    - The desktop responds with the right CORS headers
      //      (Access-Control-Allow-Origin, Allow-Private-Network for
      //      Chrome PNA), so the preflight + actual request both pass.
      const body = new URLSearchParams({
        state: params.state,
        uid: fbUser.uid,
        email: fbUser.email || "",
        name: fbUser.displayName || "",
        keyId: key.id,
        idToken,
        refreshToken: refreshToken || "",
        projectApiKey: window.CTWCAD_FIREBASE_CONFIG?.apiKey || "",
        projectId: window.CTWCAD_FIREBASE_CONFIG?.projectId || "",
      }).toString();
      const res = await fetch(`http://127.0.0.1:${params.port}/callback`, {
        method: "POST",
        mode: "cors",
        cache: "no-store",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body,
      });
      if (!res.ok && res.status !== 0) {
        throw new Error(`Desktop didn't accept the link (HTTP ${res.status}).`);
      }
      setStatus("ok");
    } catch (e) {
      console.error("[connect]", e);
      setError(
        `Couldn't reach the desktop app on 127.0.0.1:${params.port}. ` +
        `Make sure CTWCAD is still running, then click Authorize again. ` +
        `(${e?.message || String(e)})`
      );
      setStatus("err");
    }
  };

  /* -------- render variants ------------------------------------------- */

  // Bad URL params
  if (status === "err" && !paramsValid) {
    return (
      <ConnectShell title="Bad connection link">
        <p className="ct-dim">
          This page is opened by the CTWCAD desktop app. The URL is missing the
          loopback port or state token, so we can't safely send credentials.
        </p>
        <p className="ct-dim ct-mono" style={{fontSize:11}}>
          ?connect=1&amp;port=NNNN&amp;state=XXX
        </p>
      </ConnectShell>
    );
  }

  // Not signed in yet — present the sign-in card. Same component the
  // landing page uses; result feeds back into this view via App state.
  if (!signedIn) {
    return (
      <ConnectShell
        title={`Sign in to connect ${params.name}`}
        subtitle="Connecting your CTWCAD desktop install to your CTWCAD Cloud account.">
        <SignInView onSignIn={onSignIn} />
      </ConnectShell>
    );
  }

  // Signed in but not allow-listed (or still checking).
  if (allowedChecked === false) {
    return (
      <ConnectShell title="Account not on the allowlist">
        <p className="ct-dim">
          <span className="ct-mono">{user?.email}</span> isn't allowed yet. Ask
          the workspace owner to add this address from the admin panel, then
          come back here to authorize the desktop app.
        </p>
      </ConnectShell>
    );
  }

  // Success — desktop app got the credentials.
  if (status === "ok") {
    return (
      <ConnectShell title="Connected ✓">
        <p className="ct-dim">
          <strong>{params.name}</strong> is now linked to{" "}
          <span className="ct-mono">{user?.email}</span>. You can close this tab
          and return to the desktop app — it should sign you in automatically.
        </p>
        <p className="ct-dim" style={{fontSize:12, marginTop:10}}>
          You can revoke this connection any time from <strong>Settings →
          Connected devices</strong> or, for admins, the admin panel.
        </p>
      </ConnectShell>
    );
  }

  // Default: signed in, allowed → show the authorize prompt.
  return (
    <ConnectShell title={`Authorize ${params.name}?`}>
      <div className="ct-connect-row">
        <Avatar user={user} size={36}/>
        <div>
          <div><strong>{user?.name || user?.email}</strong></div>
          <div className="ct-mono ct-dim">{user?.email}</div>
        </div>
      </div>
      <ul className="ct-connect-perms">
        <li><Icon name="folder" size={13}/> Read &amp; write your CTWCAD files and folders</li>
        <li><Icon name="upload-cloud" size={13}/> Upload new versions when you save</li>
        <li><Icon name="refresh-cw" size={13}/> Stay signed in until you revoke this device</li>
      </ul>
      <p className="ct-dim" style={{fontSize:12}}>
        Sending to <span className="ct-mono">localhost:{params.port}</span>. Only
        the CTWCAD desktop app on this machine can receive this — the link
        won't work from any other computer.
      </p>
      {status === "err" && (
        <div className="ct-dim" style={{color:"var(--ct-danger)", fontSize:12}}>
          {error || "Couldn't reach the desktop app. Is CTWCAD still running?"}
        </div>
      )}
      <div className="ct-connect-actions">
        <button className="ct-btn ct-btn-ghost" onClick={() => window.close()}>
          Cancel
        </button>
        <button
          className="ct-btn ct-btn-primary"
          onClick={handleAuthorize}
          disabled={status === "authing" || allowedChecked === null}>
          <Icon name="check" size={13}/>
          {status === "authing" ? "Authorizing…" : "Authorize"}
        </button>
      </div>
    </ConnectShell>
  );
}

function ConnectShell({ title, subtitle, children }) {
  const M = window.FramerMotion.motion;
  return (
    <div className="ct-signin">
      <div className="ct-signin-bg" aria-hidden="true">
        <svg viewBox="0 0 800 800" preserveAspectRatio="xMidYMid slice">
          <defs>
            <pattern id="bg-grid-c" width="40" height="40" patternUnits="userSpaceOnUse">
              <path d="M 40 0 L 0 0 0 40" fill="none" stroke="currentColor" strokeWidth="0.4" opacity="0.18"/>
            </pattern>
          </defs>
          <rect width="800" height="800" fill="url(#bg-grid-c)"/>
        </svg>
      </div>
      <M.div className="ct-signin-card ct-connect-card"
        initial={{ opacity: 0, y: 20, scale: 0.97 }}
        animate={{ opacity: 1, y: 0, scale: 1 }}
        transition={{ type: "spring", stiffness: 240, damping: 28 }}>
        <Brandmark size={28}/>
        <h1 style={{marginTop:8}}>{title}</h1>
        {subtitle && <p className="ct-dim">{subtitle}</p>}
        {children}
      </M.div>
    </div>
  );
}

Object.assign(window, { ConnectView, ConnectShell });
