import "server-only";
import { z } from "zod";

const keySchema = z.object({
  kty: z.literal("EC"),
  crv: z.literal("P-256"),
  kid: z.string().min(1),
  x: z.string().min(1),
  y: z.string().min(1),
  d: z.string().min(1),
});
export function parseMyinfoConfig(env: Record<string, string | undefined>) {
  const mode = env.MYINFO_MODE ?? "disabled";
  if (mode === "disabled") return null;
  try {
    if (mode !== "staging" && mode !== "production") throw new Error();
    if (
      env.APPLICATION_MODE === "disabled" ||
      env.APPLICATION_MODE === "preview" ||
      (mode === "production" && env.APPLICATION_MODE !== "live")
    )
      throw new Error();
    if (mode === "staging" && env.VERCEL_ENV === "production")
      throw new Error();
    const origin = new URL(env.SITE_URL!);
    if (
      origin.pathname !== "/" ||
      origin.search ||
      origin.hash ||
      origin.username ||
      origin.password
    )
      throw new Error();
    if (
      origin.protocol !== "https:" &&
      !(
        mode === "staging" &&
        origin.protocol === "http:" &&
        origin.hostname === "localhost"
      )
    )
      throw new Error();
    const clientId = z
      .string()
      .regex(/^[a-zA-Z0-9]{32}$/)
      .parse(env.MYINFO_CLIENT_ID);
    const secret = z
      .string()
      .regex(/^[a-fA-F0-9]{64}$/)
      .parse(env.MYINFO_SESSION_SECRET);
    const signing = keySchema.parse(JSON.parse(env.MYINFO_SIGNING_JWK!));
    const encryption = keySchema.parse(JSON.parse(env.MYINFO_ENCRYPTION_JWK!));
    if (signing.kid === encryption.kid || signing.x === encryption.x)
      throw new Error();
    return {
      mode: mode as "staging" | "production",
      origin: origin.origin,
      clientId,
      secret,
      signing,
      encryption,
      issuer: `https://${mode === "staging" ? "stg-" : ""}id.singpass.gov.sg/fapi`,
      redirectUri: `${origin.origin}/api/myinfo/callback`,
    };
  } catch {
    throw new Error(
      "Invalid Myinfo server configuration. Check the Myinfo setup guide.",
    );
  }
}
export const getMyinfoConfig = () => parseMyinfoConfig(process.env);
export type MyinfoConfig = NonNullable<ReturnType<typeof getMyinfoConfig>>;
