import { OAuth2Client } from "google-auth-library";

import { publicError } from "./http.js";

const GOOGLE_ISSUERS = new Set(["accounts.google.com", "https://accounts.google.com"]);
const MAX_CLOCK_SKEW_SECONDS = 300;
const SERVICE_ACCOUNT = /^[a-z0-9][a-z0-9._-]{0,62}@[a-z0-9-]+\.iam\.gserviceaccount\.com$/;

export function createGoogleOidcVerifier({ client = new OAuth2Client(), now = Date.now } = {}) {
  if (typeof client?.verifyIdToken !== "function" || typeof now !== "function") throw new TypeError("Invalid OIDC verifier configuration.");

  return {
    async verifyRequest(req, config) {
      try {
        validateConfig(config);
        const idToken = bearerToken(req?.headers?.authorization);
        const ticket = await client.verifyIdToken({ idToken, audience: config.audience });
        const payload = ticket?.getPayload?.();
        const nowSeconds = Math.floor(Number(now()) / 1000);
        if (
          !payload
          || !GOOGLE_ISSUERS.has(payload.iss)
          || payload.aud !== config.audience
          || payload.email_verified !== true
          || !config.serviceAccounts.includes(payload.email)
          || !Number.isSafeInteger(payload.iat)
          || payload.iat > nowSeconds + MAX_CLOCK_SKEW_SECONDS
          || !Number.isSafeInteger(payload.exp)
          || payload.exp <= nowSeconds
        ) throw new Error();
        return { serviceAccount: payload.email };
      } catch {
        throw publicError(401, "unauthorized");
      }
    },
  };
}

function bearerToken(header) {
  if (typeof header !== "string") throw new Error();
  const match = /^Bearer ([\x21-\x7e]{1,8192})$/.exec(header);
  if (!match) throw new Error();
  return match[1];
}

function validateConfig(config) {
  if (
    typeof config?.audience !== "string"
    || !config.audience.startsWith("https://")
    || !Array.isArray(config.serviceAccounts)
    || config.serviceAccounts.length === 0
    || config.serviceAccounts.some((email) => !SERVICE_ACCOUNT.test(email))
  ) throw new Error();
}
