function arrBufToB64Url(buf: ArrayBuffer) {
  const b64data = btoa(String.fromCharCode(...new Uint8Array(buf)));
  return b64data.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}

function stringToBuffer(str: string) {
  const buffer = new ArrayBuffer(str.length);
  const ui8 = new Uint8Array(buffer);
  for (let i = 0, bufLen = str.length; i < bufLen; i++) {
    ui8[i] = str.charCodeAt(i);
  }
  return buffer;
}

export async function GetAccessToken(env: Env): Promise<string> {
  const claimSet = btoa(
    JSON.stringify({
      aud: "https://oauth2.googleapis.com/token",
      exp: Math.floor(Date.now() / 1000) + 120,
      iat: Math.floor(Date.now() / 1000),
      iss: env.WORKER_GSERVICEACCOUNT,
      scope: "https://www.googleapis.com/auth/cloud-platform",
    }),
  )
    .replaceAll("+", "-")
    .replaceAll("/", "_")
    .replaceAll("=", "");

  const signingKey = await crypto.subtle.importKey(
    "pkcs8",
    stringToBuffer(atob(env.STORAGE_ACCOUNT_KEY.replace(/(\r\n|\n|\r)/gm, ""))),
    { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
    false,
    ["sign"],
  );
  const signature = await crypto.subtle.sign(
    { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
    signingKey,
    stringToBuffer(`eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.${claimSet}`),
  );
  const assertion = `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.${claimSet}.${arrBufToB64Url(
    signature,
  )}`;
  const tokenRequest = await fetch("https://oauth2.googleapis.com/token", {
    body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${assertion}`,
    headers: {
      "content-type": "application/x-www-form-urlencoded",
    },
    method: "POST",
  });

  if (!tokenRequest.ok)
    throw new Error(`Failed to get access token: ${await tokenRequest.text()}`);

  return ((await tokenRequest.json()) as { [k: string]: any }).access_token;
}

export async function sendPushNotification(
  env: Env,
  title: string,
  body: string,
  token: string,
) {
  const message = {
    notification: {
      body,
      title,
    },
    token,
  };

  const notifResp = await fetch(
    "https://fcm.googleapis.com/v1/projects/car-crushers-mobile/messages:send",
    {
      body: JSON.stringify({ message }),
      headers: {
        authorization: `Bearer ${await GetAccessToken(env)}`,
        "content-type": "application/json",
      },
      method: "POST",
    },
  );

  if (!notifResp.ok) throw new Error(await notifResp.text());
}