import { TRPCError } from "@trpc/server";
import { z } from "zod";
import {
  adminProcedure,
  createTRPCRouter,
  protectedProcedure,
} from "~/server/api/trpc";
import {
  addUserStorageHandler,
  deleteUserStorageHandler,
  getAllHandler,
  getFromIdHandler,
  getMultipleWithRelations,
  getUserStorageHandler,
} from "~/server/controllers/storage.controller";
import { RateLimiterHandler } from "~/server/services/ratelimiter";
import {
  addUserStorageJunc,
  deleteFile,
  deleteUserStorageJunc,
  getNewTokenHandler,
  updateFile,
} from "~/server/services/storage.service";
import { zStorageIndexId, zUserId } from "~/server/utils/zod_types";
import { db } from "~/server/db";

const ratelimit = new RateLimiterHandler({
  maxRequests: 50,
  windowSize: 60,
  analytics: false,
});

export const storageRouter = createTRPCRouter({
  getAll: protectedProcedure.query(async () => {
    return await getAllHandler({ db });
  }),
  getStorageToken: protectedProcedure.mutation(async () => {
    const { success } = await ratelimit.limit("getStorageToken");
    if (!success) throw new TRPCError({ code: "TOO_MANY_REQUESTS" });
    const token = (await getNewTokenHandler({ db }))[0];
    if (!token) {
      throw new TRPCError({
        code: "INTERNAL_SERVER_ERROR",
        message: "Error while creating token",
      });
    }
    return token;
  }),
  getStorageTokenM: protectedProcedure
    .input(z.object({ num: z.number() }))
    .mutation(async ({ input }) => {
      const { success } = await ratelimit.limit("getStorageToken");
      if (!success) throw new TRPCError({ code: "TOO_MANY_REQUESTS" });
      return await getNewTokenHandler({ db, num: input.num });
    }),
  deleteStorage: protectedProcedure
    .input(z.object({ storageId: zStorageIndexId }))
    .mutation(async ({ input }) => {
      return await deleteFile({
        db,
        fileId: input.storageId,
        cb: async (fileId) => {
          await db
            .deleteFrom("storageindex")
            .where("id", "=", fileId)
            .execute();
        },
      });
    }),
  getUserStorage: protectedProcedure
    .input(z.object({ userId: zUserId }))
    .query(async ({ input }) => {
      return await getUserStorageHandler({ db, userId: input.userId });
    }),

  addUserStorage: protectedProcedure
    .input(
      z.object({
        userId: zUserId,
        fileId: zStorageIndexId,
      }),
    )
    .mutation(async ({ input }) => {
      return await addUserStorageHandler({
        db,
        userId: input.userId,
        storageId: input.fileId,
      });
    }),
  deleteUserStorage: protectedProcedure
    .input(z.object({ userId: zUserId, fileId: zStorageIndexId }))
    .mutation(async ({ input }) => {
      return await deleteUserStorageHandler({
        db,
        userId: input.userId,
        storageId: input.fileId,
      });
    }),
  getStorageFromId: protectedProcedure
    .input(z.object({ storageId: zStorageIndexId }))
    .query(async ({ input }) => {
      return await getFromIdHandler({
        db,
        storage_id: input.storageId,
      });
    }),
  renameFile: adminProcedure
    .input(z.object({ fileId: zStorageIndexId, name: z.string() }))
    .mutation(async ({ input }) => {
      return await updateFile({
        db,
        fileId: input.fileId,
        data: { filename: input.name },
      });
    }),
  getStorageVisibility: adminProcedure
    .input(z.object({ storageIds: zStorageIndexId.array() }))
    .query(async ({ input }) => {
      return await getMultipleWithRelations({
        db,
        ids: input.storageIds,
      });
    }),

  addStorageVisibility: adminProcedure
    .input(z.object({ storageId: zStorageIndexId, userId: zUserId }))
    .mutation(async ({ input }) => {
      await addUserStorageJunc({
        db,
        storageId: input.storageId,
        userId: input.userId,
      });
    }),
  removeStorageVisibility: adminProcedure
    .input(z.object({ storageId: zStorageIndexId, userId: zUserId }))
    .mutation(async ({ input }) => {
      await deleteUserStorageJunc({
        db,
        storageId: input.storageId,
        userId: input.userId,
      });
    }),
  resetStorageVisibility: adminProcedure
    .input(z.object({ storageId: zStorageIndexId.array() }))
    .mutation(async ({ input }) => {
      await db
        .deleteFrom("users_storage")
        .where("storageid", "in", input.storageId)
        .execute();
      return "ok";
    }),
});
