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

function errorResponse(error: string, status: number): Response {
  return new Response(JSON.stringify({ error }), {
    headers: {
      "content-type": "application/json",
    },
    status,
  });
}

export async function onRequestPost(context: RequestContext) {
  const { filename, filesize, turnstileResponse, usernames } =
    context.data.body;

  if (!context.data.current_user) {
    if (typeof turnstileResponse !== "string")
      return errorResponse("You must complete the captcha", 401);

    const turnstileAPIResponse = await fetch(
      "https://challenges.cloudflare.com/turnstile/v0/siteverify",
      {
        body: JSON.stringify({
          response: turnstileResponse,
          secret: context.env.TURNSTILE_SECRETKEY,
        }),
        headers: {
          "content-type": "application/json",
        },
        method: "POST",
      }
    );

    const { success }: { success: boolean } = await turnstileAPIResponse.json();

    if (!success) return errorResponse("Captcha test failed", 403);
  }

  if (!Array.isArray(usernames))
    return errorResponse("Usernames must be type of array", 400);

  if (typeof filename !== "string")
    return errorResponse("Invalid file name", 400);

  if (typeof filesize !== "number" || filesize < 0 || filesize > 536870912)
    return errorResponse("Invalid file size", 400);

  if (!usernames.length || usernames.length > 20)
    return errorResponse(
      "Number of usernames provided must be between 1 and 20",
      400
    );

  for (const username of usernames) {
    if (
      username.length < 3 ||
      username.length > 20 ||
      username.match(/_/g)?.length > 1
    )
      return errorResponse(`Username "${username}" is invalid`, 400);
  }

  const rbxSearchReq = await fetch(
    "https://users.roblox.com/v1/usernames/users",
    {
      body: JSON.stringify({
        usernames,
        excludeBannedUsers: true,
      }),
      headers: {
        "content-type": "application/json",
      },
      method: "POST",
    }
  );

  if (!rbxSearchReq.ok)
    return errorResponse(
      "Failed to locate Roblox users due to upstream error",
      500
    );

  const rbxSearchData: { data: { [k: string]: any }[] } =
    await rbxSearchReq.json();

  if (rbxSearchData.data.length < usernames.length) {
    const missingUsers = [];

    for (const userData of rbxSearchData.data) {
      if (!usernames.includes(userData.requestedUsername))
        missingUsers.push(userData.requestedUsername);
    }

    return errorResponse(
      `The following users do not exist or are banned from Roblox: ${missingUsers.toString()}`,
      400
    );
  }

  const metaIDs = [];
  const metaNames = [];

  for (const data of rbxSearchData.data) {
    metaIDs.push(data.id);
    metaNames.push(data.name);
  }

  const fileParts = filename.split(".");
  let fileExt = fileParts[fileParts.length - 1];

  if (
    fileParts.length < 2 ||
    ![
      "mkv",
      "mp4",
      "wmv",
      "jpg",
      "png",
      "m4v",
      "jpeg",
      "jfif",
      "gif",
      "webm",
      "heif",
      "heic",
      "webp",
      "mov",
    ].includes(fileExt.toLowerCase())
  )
    return errorResponse("This type of file cannot be uploaded", 415);

  const fileKey = `${crypto.randomUUID().replaceAll("-", "")}/${crypto
    .randomUUID()
    .replaceAll("-", "")}${context.request.headers.get("cf-ray")}${Date.now()}`;

  const reportId = `${Date.now()}${context.request.headers.get(
    "cf-ray"
  )}${crypto.randomUUID().replaceAll("-", "")}`;

  const uploadUrl = await GenerateUploadURL(
    context.env,
    fileKey,
    filesize,
    fileExt
  );

  await context.env.DATA.put(
    `reportprocessing_${reportId}`,
    context.data.current_user.id,
    { expirationTtl: 3600 }
  );
  await context.env.DATA.put(
    `report_${reportId}`,
    JSON.stringify({
      attachment: `${fileKey}.${
        ["mkv", "mov", "wmv"].includes(fileExt.toLowerCase()) ? "mp4" : fileExt
      }`,
      reporter: context.data.current_user,
      target_ids: metaIDs,
      target_usernames: metaNames,
    }),
    {
      metadata: {
        i: context.data.current_user.id,
        r: metaIDs.toString(),
        p: true,
        s: `${context.data.current_user.username}#${context.data.current_user.discriminator}`,
        u: metaNames.toString(),
      },
    }
  );

  return new Response(JSON.stringify({ id: reportId, upload_url: uploadUrl }), {
    headers: {
      "content-type": "application/json",
    },
  });
}