import { GenerateUploadURL } from "../../gcloud.js";

const allowedFileTypes = [
  "image/gif",
  "image/heic",
  "image/heif",
  "image/jpeg",
  "image/png",
  "video/mp4",
  "video/webm",
];

export async function onRequestPost(context: RequestContext) {
  if (
    !context.data.current_user?.permissions ||
    ![1 << 0, 1 << 2, 1 << 11].find(
      (p) => context.data.current_user.permissions & p
    )
  )
    return new Response('{"error":"Forbidden"}', {
      headers: {
        "content-type": "application/json",
      },
      status: 403,
    });

  if (
    context.request.headers
      .get("content-type")
      ?.startsWith("multipart/form-data; boundary=")
  )
    return new Response('{"error":"Invalid content type"}', {
      headers: {
        "content-type": "application/json",
      },
      status: 400,
    });

  let body: FormData;

  try {
    body = await context.request.formData();
  } catch {
    return new Response('{"error":"Invalid form data"}', {
      headers: {
        "content-type": "application/json",
      },
      status: 400,
    });
  }

  if (
    ![
      "verbal",
      "warn",
      "mute",
      "ban_temp",
      "ban_perm",
      "ban_unappealable",
    ].includes(body.get("punishment") as string)
  )
    return new Response('{"error":"Invalid punishment"}', {
      headers: {
        "content-type": "application/json",
      },
      status: 400,
    });

  if (!(body.get("user") as string).match(/^\d{17,19}$/))
    return new Response('{"error":"Invalid user"}', {
      headers: {
        "content-type": "application/json",
      },
      status: 400,
    });

  // @ts-expect-error
  const files: File[] = body.keys().find((key) => key.match(/^files\[\d]$/));
  const urlPromises = [];

  for (const file of files) {
    if (!allowedFileTypes.includes(file.type))
      return new Response(`{"error":"File ${file.name} is not valid"}`, {
        headers: {
          "content-type": "application/json",
        },
        status: 415,
      });

    const attachmentKey = `${Date.now()}${Math.round(
      Math.random() * 10000000
    ).toString()}/${file.name}`;

    urlPromises.push(
      GenerateUploadURL(
        context.env,
        attachmentKey,
        file.size,
        (allowedFileTypes.find((t) => t === file.type) as string).split("/")[1]
      )
    );
  }

  const settledURLPromises = await Promise.allSettled(urlPromises);

  if (settledURLPromises.find((p) => p.status === "rejected"))
    return new Response('{"error":"Failed to process one or more files"}', {
      headers: {
        "content-type": "application/json",
      },
      status: 500,
    });

  const infractionId = `${body.get("user")}${Date.now()}${
    context.request.headers.get("cf-ray")?.split("-")[0]
  }`;
  const uploadReqs = [];

  for (let i = 0; i < files.length; i++) {
    uploadReqs.push(
      fetch(settledURLPromises[i] as unknown as string, {
        body: files[i],
        method: "PUT",
      })
    );
  }

  await context.env.DATA.put(
    infractionId,
    JSON.stringify({
      created_at: Date.now(),
      moderator: context.data.current_user.id,
    })
  );

  return new Response(null, {
    status: 204,
  });
}
