import { z } from "zod";

import {
  createTRPCRouter,
  protectedProcedure,
  publicProcedure,
} from "~/server/api/trpc";
import {
  DeleteObjectCommand,
  GetObjectCommand,
  PutObjectCommand,
} from "@aws-sdk/client-s3";
import { env } from "~/env.mjs";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { TRPCError } from "@trpc/server";

export const videoRouter = createTRPCRouter({
  getAll: protectedProcedure.query(
    async ({ ctx: { prisma, session, s3, posthog } }) => {
      const videos = await prisma.video.findMany({
        where: {
          userId: session.user.id,
        },
      });

      posthog?.capture({
        distinctId: session.user.id,
        event: "viewing video list",
        properties: {
          videoAmount: videos.length,
        },
      });
      void posthog?.shutdownAsync();

      const videosWithThumbnailUrl = await Promise.all(
        videos.map(async (video) => {
          const thumbnailUrl = await getSignedUrl(
            s3,
            new GetObjectCommand({
              Bucket: env.AWS_BUCKET_NAME,
              Key: video.userId + "/" + video.id + "-thumbnail",
            })
          );

          return { ...video, thumbnailUrl };
        })
      );

      return videosWithThumbnailUrl;
    }
  ),
  get: publicProcedure
    .input(z.object({ videoId: z.string() }))
    .query(async ({ ctx, input }) => {
      const { s3, posthog, session, prisma } = ctx;
      const video = await prisma.video.findUnique({
        where: {
          id: input.videoId,
        },
        include: {
          user: true,
        },
      });
      if (!video) {
        throw new TRPCError({ code: "NOT_FOUND" });
      }

      if (video.userId !== session?.user.id && !video.sharing) {
        throw new TRPCError({ code: "FORBIDDEN" });
      }

      if (session) {
        posthog?.capture({
          distinctId: session.user.id,
          event: "viewing video",
          properties: {
            videoId: video.id,
            videoCreatedAt: video.createdAt,
            videoUpdatedAt: video.updatedAt,
            videoUser: video.user.id,
            videoSharing: video.sharing,
            videoDeleteAfterLinkExpires: video.delete_after_link_expires,
            videoShareLinkExpiresAt: video.shareLinkExpiresAt,
          },
        });
        void posthog?.shutdownAsync();
      }

      const getObjectCommand = new GetObjectCommand({
        Bucket: env.AWS_BUCKET_NAME,
        Key: video.userId + "/" + video.id,
      });

      const signedUrl = await getSignedUrl(s3, getObjectCommand);

      return { ...video, video_url: signedUrl };
    }),
  getUploadUrl: protectedProcedure
    .input(z.object({ key: z.string() }))
    .mutation(async ({ ctx: { prisma, session, s3, posthog }, input }) => {
      const { key } = input;

      const videos = await prisma.video.findMany({
        where: {
          userId: session.user.id,
        },
      });

      if (
        videos.length >= 10 &&
        session.user.stripeSubscriptionStatus !== "active"
      ) {
        posthog?.capture({
          distinctId: session.user.id,
          event: "hit video upload limit",
          properties: {
            videoAmount: videos.length,
            stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
          },
        });
        void posthog?.shutdownAsync();

        throw new TRPCError({
          code: "FORBIDDEN",
          message:
            "Sorry, you have reached the maximum video upload limit on our free tier. Please upgrade to upload more.",
        });
      }

      posthog?.capture({
        distinctId: session.user.id,
        event: "uploading video",
        properties: {
          videoAmount: videos.length,
          stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
        },
      });
      void posthog?.shutdownAsync();

      const video = await prisma.video.create({
        data: {
          userId: session.user.id,
          title: key,
        },
      });

      const signedVideoUrl = await getSignedUrl(
        s3,
        new PutObjectCommand({
          Bucket: env.AWS_BUCKET_NAME,
          Key: session.user.id + "/" + video.id,
        })
      );

      const signedThumbnailUrl = await getSignedUrl(
        s3,
        new PutObjectCommand({
          Bucket: env.AWS_BUCKET_NAME,
          Key: video.userId + "/" + video.id + "-thumbnail",
        })
      );

      return {
        success: true,
        id: video.id,
        signedVideoUrl,
        signedThumbnailUrl,
      };
    }),
  setSharing: protectedProcedure
    .input(z.object({ videoId: z.string(), sharing: z.boolean() }))
    .mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
      const updateVideo = await prisma.video.updateMany({
        where: {
          id: input.videoId,
          userId: session.user.id,
        },
        data: {
          sharing: input.sharing,
        },
      });

      if (updateVideo.count === 0) {
        throw new TRPCError({ code: "FORBIDDEN" });
      }

      posthog?.capture({
        distinctId: session.user.id,
        event: "update video setSharing",
        properties: {
          videoId: input.videoId,
          videoSharing: input.sharing,
        },
      });
      void posthog?.shutdownAsync();

      return {
        success: true,
        updateVideo,
      };
    }),
  setDeleteAfterLinkExpires: protectedProcedure
    .input(
      z.object({ videoId: z.string(), delete_after_link_expires: z.boolean() })
    )
    .mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
      const updateVideo = await prisma.video.updateMany({
        where: {
          id: input.videoId,
          userId: session.user.id,
        },
        data: {
          delete_after_link_expires: input.delete_after_link_expires,
        },
      });

      if (updateVideo.count === 0) {
        throw new TRPCError({ code: "FORBIDDEN" });
      }

      posthog?.capture({
        distinctId: session.user.id,
        event: "update video delete_after_link_expires",
        properties: {
          videoId: input.videoId,
          delete_after_link_expires: input.delete_after_link_expires,
        },
      });
      void posthog?.shutdownAsync();

      return {
        success: true,
        updateVideo,
      };
    }),
  setShareLinkExpiresAt: protectedProcedure
    .input(
      z.object({
        videoId: z.string(),
        shareLinkExpiresAt: z.nullable(z.date()),
      })
    )
    .mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
      const updateVideo = await prisma.video.updateMany({
        where: {
          id: input.videoId,
          userId: session.user.id,
        },
        data: {
          shareLinkExpiresAt: input.shareLinkExpiresAt,
        },
      });

      if (updateVideo.count === 0) {
        throw new TRPCError({ code: "FORBIDDEN" });
      }

      posthog?.capture({
        distinctId: session.user.id,
        event: "update video shareLinkExpiresAt",
        properties: {
          videoId: input.videoId,
          shareLinkExpiresAt: input.shareLinkExpiresAt,
        },
      });
      void posthog?.shutdownAsync();

      return {
        success: true,
        updateVideo,
      };
    }),
  renameVideo: protectedProcedure
    .input(
      z.object({
        videoId: z.string(),
        title: z.string(),
      })
    )
    .mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
      const updateVideo = await prisma.video.updateMany({
        where: {
          id: input.videoId,
          userId: session.user.id,
        },
        data: {
          title: input.title,
        },
      });

      if (updateVideo.count === 0) {
        throw new TRPCError({ code: "FORBIDDEN" });
      }

      posthog?.capture({
        distinctId: session.user.id,
        event: "update video title",
        properties: {
          videoId: input.videoId,
          title: input.title,
        },
      });
      void posthog?.shutdownAsync();

      return {
        success: true,
        updateVideo,
      };
    }),
  deleteVideo: protectedProcedure
    .input(
      z.object({
        videoId: z.string(),
      })
    )
    .mutation(async ({ ctx: { prisma, session, s3, posthog }, input }) => {
      const deleteVideo = await prisma.video.deleteMany({
        where: {
          id: input.videoId,
          userId: session.user.id,
        },
      });

      if (deleteVideo.count === 0) {
        throw new TRPCError({ code: "FORBIDDEN" });
      }

      posthog?.capture({
        distinctId: session.user.id,
        event: "video delete",
        properties: {
          videoId: input.videoId,
        },
      });
      void posthog?.shutdownAsync();

      const deleteVideoObject = await s3.send(
        new DeleteObjectCommand({
          Bucket: env.AWS_BUCKET_NAME,
          Key: session.user.id + "/" + input.videoId,
        })
      );

      const deleteThumbnailObject = await s3.send(
        new DeleteObjectCommand({
          Bucket: env.AWS_BUCKET_NAME,
          Key: session.user.id + "/" + input.videoId + "-thumbnail",
        })
      );

      return {
        success: true,
        deleteVideo,
        deleteVideoObject,
        deleteThumbnailObject,
      };
    }),
});
