import type { NextApiRequest, NextApiResponse } from "next";
import readFile from "@/lib/api/storage/readFile";
import { prisma } from "@/lib/api/db";
import { ArchivedFormat } from "@/types/global";
import verifyUser from "@/lib/api/verifyUser";
import getPermission from "@/lib/api/getPermission";
import { UsersAndCollections } from "@prisma/client";
import formidable from "formidable";
import createFile from "@/lib/api/storage/createFile";
import fs from "fs";
import verifyToken from "@/lib/api/verifyToken";
import generatePreview from "@/lib/api/generatePreview";
import createFolder from "@/lib/api/storage/createFolder";

export const config = {
  api: {
    bodyParser: false,
  },
};

export default async function Index(req: NextApiRequest, res: NextApiResponse) {
  const linkId = Number(req.query.linkId);
  const format = Number(req.query.format);
  const isPreview = Boolean(req.query.preview);

  let suffix: string;

  if (format === ArchivedFormat.png) suffix = ".png";
  else if (format === ArchivedFormat.jpeg) suffix = ".jpeg";
  else if (format === ArchivedFormat.pdf) suffix = ".pdf";
  else if (format === ArchivedFormat.readability) suffix = "_readability.json";
  else if (format === ArchivedFormat.monolith) suffix = ".html";

  //@ts-ignore
  if (!linkId || !suffix)
    return res.status(401).json({ response: "Invalid parameters." });

  if (req.method === "GET") {
    const token = await verifyToken({ req });
    const userId = typeof token === "string" ? undefined : token?.id;

    const collectionIsAccessible = await prisma.collection.findFirst({
      where: {
        links: {
          some: {
            id: linkId,
          },
        },
        OR: [
          { ownerId: userId || -1 },
          { members: { some: { userId: userId || -1 } } },
          { isPublic: true },
        ],
      },
    });

    if (!collectionIsAccessible)
      return res
        .status(401)
        .json({ response: "You don't have access to this collection." });

    if (isPreview) {
      const { file, contentType, status } = await readFile(
        `archives/preview/${collectionIsAccessible.id}/${linkId}.jpeg`
      );

      res.setHeader("Content-Type", contentType).status(status as number);

      return res.send(file);
    } else {
      const { file, contentType, status } = await readFile(
        `archives/${collectionIsAccessible.id}/${linkId + suffix}`
      );

      res.setHeader("Content-Type", contentType).status(status as number);

      return res.send(file);
    }
  } else if (req.method === "POST") {
    const user = await verifyUser({ req, res });
    if (!user) return;

    const collectionPermissions = await getPermission({
      userId: user.id,
      linkId,
    });

    if (!collectionPermissions)
      return { response: "Collection is not accessible.", status: 400 };

    const memberHasAccess = collectionPermissions.members.some(
      (e: UsersAndCollections) => e.userId === user.id && e.canCreate
    );

    if (!(collectionPermissions.ownerId === user.id || memberHasAccess))
      return { response: "Collection is not accessible.", status: 400 };

    // await uploadHandler(linkId, )

    const MAX_LINKS_PER_USER = Number(process.env.MAX_LINKS_PER_USER || 30000);

    const numberOfLinksTheUserHas = await prisma.link.count({
      where: {
        collection: {
          ownerId: user.id,
        },
      },
    });

    if (numberOfLinksTheUserHas > MAX_LINKS_PER_USER)
      return {
        response: `Each collection owner can only have a maximum of ${MAX_LINKS_PER_USER} Links.`,
        status: 400,
      };

    const NEXT_PUBLIC_MAX_FILE_BUFFER = Number(
      process.env.NEXT_PUBLIC_MAX_FILE_BUFFER || 10
    );

    const form = formidable({
      maxFields: 1,
      maxFiles: 1,
      maxFileSize: NEXT_PUBLIC_MAX_FILE_BUFFER * 1024 * 1024,
    });

    form.parse(req, async (err, fields, files) => {
      const allowedMIMETypes = [
        "application/pdf",
        "image/png",
        "image/jpg",
        "image/jpeg",
      ];

      if (
        err ||
        !files.file ||
        !files.file[0] ||
        !allowedMIMETypes.includes(files.file[0].mimetype || "")
      ) {
        // Handle parsing error
        return res.status(400).json({
          response: `Sorry, we couldn't process your file. Please ensure it's a PDF, PNG, or JPG format and doesn't exceed ${NEXT_PUBLIC_MAX_FILE_BUFFER}MB.`,
        });
      } else {
        const fileBuffer = fs.readFileSync(files.file[0].filepath);

        if (
          Buffer.byteLength(fileBuffer) >
          1024 * 1024 * Number(NEXT_PUBLIC_MAX_FILE_BUFFER)
        )
          return res.status(400).json({
            response: `Sorry, we couldn't process your file. Please ensure it's a PDF, PNG, or JPG format and doesn't exceed ${NEXT_PUBLIC_MAX_FILE_BUFFER}MB.`,
          });

        const linkStillExists = await prisma.link.findUnique({
          where: { id: linkId },
        });

        if (linkStillExists && files.file[0].mimetype?.includes("image")) {
          const collectionId = collectionPermissions.id as number;
          createFolder({
            filePath: `archives/preview/${collectionId}`,
          });

          generatePreview(fileBuffer, collectionId, linkId);
        }

        if (linkStillExists) {
          await createFile({
            filePath: `archives/${collectionPermissions.id}/${linkId + suffix}`,
            data: fileBuffer,
          });

          await prisma.link.update({
            where: { id: linkId },
            data: {
              preview: files.file[0].mimetype?.includes("pdf")
                ? "unavailable"
                : undefined,
              image: files.file[0].mimetype?.includes("image")
                ? `archives/${collectionPermissions.id}/${linkId + suffix}`
                : null,
              pdf: files.file[0].mimetype?.includes("pdf")
                ? `archives/${collectionPermissions.id}/${linkId + suffix}`
                : null,
              lastPreserved: new Date().toISOString(),
            },
          });
        }

        fs.unlinkSync(files.file[0].filepath);
      }

      return res.status(200).json({
        response: files,
      });
    });
  }
}
