import { Router, type Response } from "express";
import { z } from "zod";
import { requireBoardAccess } from "../lib/authz";
import { prisma } from "../lib/prisma";
import { emitToBoard } from "../lib/socketBus";
import { HttpError } from "../middleware/errorHandler";
import { requireAuth } from "../middleware/requireAuth";

export const commentsRouter = Router();
commentsRouter.use(requireAuth);

const cardDetailInclude = {
  labels: { include: { label: true } },
  members: { include: { user: true } },
  checklists: { include: { items: { include: { assignees: { include: { user: true } } } } } },
  comments: { include: { author: true, reactions: true, mentions: true, seenBy: true }, orderBy: { createdAt: "asc" as const } },
  attachments: { include: { uploader: true } },
  customFieldValues: { include: { customField: true } },
  status: true,
};

async function loadCommentWithCard(commentId: string) {
  const comment = await prisma.comment.findUnique({
    where: { id: commentId },
    include: { card: true },
  });
  if (!comment) throw new HttpError(404, "Comment not found");
  return comment;
}

async function respondWithCard(cardId: string, boardId: string, res: Response) {
  const updated = await prisma.card.findUnique({ where: { id: cardId }, include: cardDetailInclude });
  emitToBoard(boardId, "card:updated", { card: updated });
  res.json({ card: updated });
}

const updateCommentSchema = z.object({ bodyHtml: z.string().min(1) });

commentsRouter.patch("/:id", async (req, res, next) => {
  try {
    const comment = await loadCommentWithCard(req.params.id);
    if (comment.authorId !== req.userId) {
      throw new HttpError(403, "You can only edit your own comments");
    }

    const data = updateCommentSchema.parse(req.body);
    await prisma.comment.update({
      where: { id: req.params.id },
      data: { bodyHtml: data.bodyHtml, isEdited: true },
    });

    await respondWithCard(comment.cardId, comment.card.boardId, res);
  } catch (err) {
    next(err);
  }
});

commentsRouter.delete("/:id", async (req, res, next) => {
  try {
    const comment = await loadCommentWithCard(req.params.id);
    if (comment.authorId !== req.userId) {
      await requireBoardAccess(req.userId!, comment.card.boardId, "ADMIN");
    }

    await prisma.comment.delete({ where: { id: req.params.id } });
    await respondWithCard(comment.cardId, comment.card.boardId, res);
  } catch (err) {
    next(err);
  }
});

const reactionSchema = z.object({ emoji: z.string().min(1).max(8) });

commentsRouter.post("/:id/reactions", async (req, res, next) => {
  try {
    const comment = await loadCommentWithCard(req.params.id);
    await requireBoardAccess(req.userId!, comment.card.boardId, "MEMBER");

    const { emoji } = reactionSchema.parse(req.body);
    await prisma.commentReaction.upsert({
      where: { commentId_userId_emoji: { commentId: req.params.id, userId: req.userId!, emoji } },
      update: {},
      create: { commentId: req.params.id, userId: req.userId!, emoji },
    });

    await respondWithCard(comment.cardId, comment.card.boardId, res);
  } catch (err) {
    next(err);
  }
});

commentsRouter.delete("/:id/reactions/:emoji", async (req, res, next) => {
  try {
    const comment = await loadCommentWithCard(req.params.id);
    await requireBoardAccess(req.userId!, comment.card.boardId, "MEMBER");

    await prisma.commentReaction.delete({
      where: {
        commentId_userId_emoji: {
          commentId: req.params.id,
          userId: req.userId!,
          emoji: req.params.emoji,
        },
      },
    });

    await respondWithCard(comment.cardId, comment.card.boardId, res);
  } catch (err) {
    next(err);
  }
});
