import * as oidc from "openid-client";
import { data, redirect } from "react-router";

import { HeadplaneConfig } from "~/server/config/config-schema";
import { createOidcStateCookie } from "~/utils/oidc-state";

import type { Route } from "./+types/oidc-start";

export async function loader({ request, context }: Route.LoaderArgs) {
  try {
    await context.auth.require(request);
    return redirect("/");
  } catch {}

  const oidcConnector = await context.oidc?.connector.get();
  if (!oidcConnector?.isValid) {
    throw data("OIDC is not enabled or misconfigured", { status: 501 });
  }

  const cookie = createOidcStateCookie(context.config);
  const redirect_uri = getRedirectUri(context.config, request);

  const nonce = oidc.randomNonce();
  const verifier = oidc.randomPKCECodeVerifier();
  const state = oidc.randomState();

  const url = oidc.buildAuthorizationUrl(oidcConnector.client, {
    ...oidcConnector.extraParams,
    scope: oidcConnector.scope,
    redirect_uri,
    state,
    nonce,
    ...(oidcConnector.usePKCE
      ? {
          code_challenge_method: "S256",
          code_challenge: await oidc.calculatePKCECodeChallenge(verifier),
        }
      : {}),
  });

  return redirect(url.href, {
    status: 302,
    headers: {
      "Set-Cookie": await cookie.serialize({
        state,
        nonce,
        verifier,
        redirect_uri,
      }),
    },
  });
}

function getRedirectUri(config: HeadplaneConfig, req: Request): string {
  if (config.server.base_url != null) {
    const url = new URL(`${__PREFIX__}/oidc/callback`, config.server.base_url);
    return url.href;
  }

  if (config.oidc?.redirect_uri != null) {
    const url = new URL(`${__PREFIX__}/oidc/callback`, config.oidc.redirect_uri);
    return url.href;
  }

  const url = new URL(`${__PREFIX__}/oidc/callback`, req.url);
  let host = req.headers.get("Host");
  if (!host) {
    host = req.headers.get("X-Forwarded-Host");
  }

  if (!host) {
    throw data("Cannot determine redirect URI: no Host or X-Forwarded-Host header", {
      status: 500,
    });
  }

  const proto = req.headers.get("X-Forwarded-Proto");
  url.protocol = proto ?? "http:";
  url.host = host;
  return url.href;
}
