import { GenerateUploadURL } from "../../gcloud.js";
import { jsonError } from "../../common.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 jsonError("Forbidden", 403);

  if (
    !context.request.headers
      .get("content-type")
      ?.toLowerCase()
      .startsWith("multipart/form-data;")
  )
    return jsonError("Invalid content type", 400);

  let body: FormData;

  try {
    body = await context.request.formData();
  } catch {
    return jsonError("Invalid form data", 400);
  }

  if (
    ![
      "verbal",
      "warn",
      "mute",
      "ban_temp",
      "ban_perm",
      "ban_unappealable",
    ].includes(body.get("punishment") as string)
  )
    return jsonError("Invalid punishment", 400);

  if (!(body.get("user") as string).match(/^\d{17,19}$/))
    return jsonError("Invalid user", 400);

  const fileKeys = Array.from(body.keys()).filter((key) =>
    key.match(/^file\d$/),
  );

  if (fileKeys.length > 5)
    return jsonError("Only up to five files are allowed per infraction", 400);

  const files: File[] = [];

  for (const fileKey of fileKeys) {
    const file = body.get(fileKey);

    if (!(file instanceof File))
      return jsonError("File keys can only contain files", 400);

    files.push(file);
  }

  const urlPromises = [];
  const origin = context.request.headers.get("Origin");

  if (!origin) return jsonError("Origin header missing", 400);

  for (const file of files) {
    if (!allowedFileTypes.includes(file.type))
      return jsonError(`File ${file.name} is not valid`, 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],
        origin,
      ),
    );
  }

  const settledURLPromises = await Promise.allSettled(urlPromises);

  if (settledURLPromises.find((p) => p.status === "rejected"))
    return jsonError("Failed to process one or more files", 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,
  });
}