import { z } from 'zod';
import { Vote } from '@prisma/client';
import { TRPCError } from '@trpc/server';

import { createProtectedRouter } from './context';

import type { QuestionComment } from '~/types/questions';

export const questionsQuestionCommentRouter = createProtectedRouter()
  .query('getQuestionComments', {
    input: z.object({
      questionId: z.string(),
    }),
    async resolve({ ctx, input }) {
      const { questionId } = input;
      const questionCommentsData =
        await ctx.prisma.questionsQuestionComment.findMany({
          include: {
            user: {
              select: {
                image: true,
                name: true,
              },
            },
            votes: true,
          },
          orderBy: {
            createdAt: 'desc',
          },
          where: {
            questionId,
          },
        });
      return questionCommentsData.map((data) => {
        const votes: number = data.votes.reduce(
          (previousValue: number, currentValue) => {
            let result: number = previousValue;

            switch (currentValue.vote) {
              case Vote.UPVOTE:
                result += 1;
                break;
              case Vote.DOWNVOTE:
                result -= 1;
                break;
            }
            return result;
          },
          0,
        );

        const questionComment: QuestionComment = {
          content: data.content,
          createdAt: data.createdAt,
          id: data.id,
          numVotes: votes,
          user: data.user?.name ?? '',
          userImage: data.user?.image ?? '',
        };
        return questionComment;
      });
    },
  })
  .mutation('create', {
    input: z.object({
      content: z.string(),
      questionId: z.string(),
    }),
    async resolve({ ctx, input }) {
      const userId = ctx.session?.user?.id;

      const { content, questionId } = input;

      return await ctx.prisma.questionsQuestionComment.create({
        data: {
          content,
          questionId,
          userId,
        },
      });
    },
  })
  .mutation('update', {
    input: z.object({
      content: z.string().optional(),
      id: z.string(),
    }),
    async resolve({ ctx, input }) {
      const userId = ctx.session?.user?.id;

      const { content } = input;

      const questionCommentToUpdate =
        await ctx.prisma.questionsQuestionComment.findUnique({
          where: {
            id: input.id,
          },
        });

      if (questionCommentToUpdate?.id !== userId) {
        throw new TRPCError({
          code: 'UNAUTHORIZED',
          message: 'User have no authorization to record.',
        });
      }

      return await ctx.prisma.questionsQuestionComment.update({
        data: {
          content,
        },
        where: {
          id: input.id,
        },
      });
    },
  })
  .mutation('delete', {
    input: z.object({
      id: z.string(),
    }),
    async resolve({ ctx, input }) {
      const userId = ctx.session?.user?.id;

      const questionCommentToDelete =
        await ctx.prisma.questionsQuestionComment.findUnique({
          where: {
            id: input.id,
          },
        });

      if (questionCommentToDelete?.id !== userId) {
        throw new TRPCError({
          code: 'UNAUTHORIZED',
          message: 'User have no authorization to record.',
        });
      }

      return await ctx.prisma.questionsQuestionComment.delete({
        where: {
          id: input.id,
        },
      });
    },
  })
  .query('getVote', {
    input: z.object({
      questionCommentId: z.string(),
    }),
    async resolve({ ctx, input }) {
      const userId = ctx.session?.user?.id;
      const { questionCommentId } = input;

      return await ctx.prisma.questionsQuestionCommentVote.findUnique({
        where: {
          questionCommentId_userId: { questionCommentId, userId },
        },
      });
    },
  })
  .mutation('createVote', {
    input: z.object({
      questionCommentId: z.string(),
      vote: z.nativeEnum(Vote),
    }),
    async resolve({ ctx, input }) {
      const userId = ctx.session?.user?.id;
      const { questionCommentId, vote } = input;

      const incrementValue: number = vote === Vote.UPVOTE ? 1 : -1;

      const [questionCommentVote] = await ctx.prisma.$transaction([
        ctx.prisma.questionsQuestionCommentVote.create({
          data: {
            questionCommentId,
            userId,
            vote,
          },
        }),
        ctx.prisma.questionsQuestionComment.update({
          data: {
            upvotes: {
              increment: incrementValue,
            },
          },
          where: {
            id: questionCommentId,
          },
        }),
      ]);
      return questionCommentVote;
    },
  })
  .mutation('updateVote', {
    input: z.object({
      id: z.string(),
      vote: z.nativeEnum(Vote),
    }),
    async resolve({ ctx, input }) {
      const userId = ctx.session?.user?.id;
      const { id, vote } = input;

      const voteToUpdate =
        await ctx.prisma.questionsQuestionCommentVote.findUnique({
          where: {
            id: input.id,
          },
        });

      if (voteToUpdate?.userId !== userId) {
        throw new TRPCError({
          code: 'UNAUTHORIZED',
          message: 'User have no authorization to record.',
        });
      }

      const incrementValue = vote === Vote.UPVOTE ? 2 : -2;

      const [questionCommentVote] = await ctx.prisma.$transaction([
        ctx.prisma.questionsQuestionCommentVote.update({
          data: {
            vote,
          },
          where: {
            id,
          },
        }),
        ctx.prisma.questionsQuestionComment.update({
          data: {
            upvotes: {
              increment: incrementValue,
            },
          },
          where: {
            id: voteToUpdate.questionCommentId,
          },
        }),
      ]);

      return questionCommentVote;
    },
  })
  .mutation('deleteVote', {
    input: z.object({
      id: z.string(),
    }),
    async resolve({ ctx, input }) {
      const userId = ctx.session?.user?.id;

      const voteToDelete =
        await ctx.prisma.questionsQuestionCommentVote.findUnique({
          where: {
            id: input.id,
          },
        });

      if (voteToDelete?.userId !== userId) {
        throw new TRPCError({
          code: 'UNAUTHORIZED',
          message: 'User have no authorization to record.',
        });
      }

      const incrementValue = voteToDelete.vote === Vote.UPVOTE ? -1 : 1;

      const [questionCommentVote] = await ctx.prisma.$transaction([
        ctx.prisma.questionsQuestionCommentVote.delete({
          where: {
            id: input.id,
          },
        }),
        ctx.prisma.questionsQuestionComment.update({
          data: {
            upvotes: {
              increment: incrementValue,
            },
          },
          where: {
            id: voteToDelete.questionCommentId,
          },
        }),
      ]);
      return questionCommentVote;
    },
  });