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

export const checklistItemsRouter = Router();
checklistItemsRouter.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 loadItemWithCard(itemId: string) {
  const item = await prisma.checklistItem.findUnique({
    where: { id: itemId },
    include: { checklist: { include: { card: true } } },
  });
  if (!item) throw new HttpError(404, "Checklist item not found");
  return item;
}

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 updateItemSchema = z.object({
  text: z.string().min(1).max(500).optional(),
  isCompleted: z.boolean().optional(),
  dueDate: z.string().datetime().nullable().optional(),
  beforePosition: z.number().optional(),
  afterPosition: z.number().optional(),
});

checklistItemsRouter.patch("/:id", async (req, res, next) => {
  try {
    const item = await loadItemWithCard(req.params.id);
    const boardId = item.checklist.card.boardId;
    await requireBoardAccess(req.userId!, boardId, "MEMBER");

    const data = updateItemSchema.parse(req.body);
    const { beforePosition, afterPosition, ...rest } = data;

    await prisma.checklistItem.update({
      where: { id: req.params.id },
      data: {
        ...rest,
        ...(beforePosition !== undefined || afterPosition !== undefined
          ? { position: positionBetween(beforePosition, afterPosition) }
          : {}),
      },
    });

    await respondWithCard(item.checklist.cardId, boardId, res);
  } catch (err) {
    next(err);
  }
});

checklistItemsRouter.delete("/:id", async (req, res, next) => {
  try {
    const item = await loadItemWithCard(req.params.id);
    const boardId = item.checklist.card.boardId;
    await requireBoardAccess(req.userId!, boardId, "MEMBER");

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

checklistItemsRouter.post("/:id/assignees/:userId", async (req, res, next) => {
  try {
    const item = await loadItemWithCard(req.params.id);
    const boardId = item.checklist.card.boardId;
    await requireBoardAccess(req.userId!, boardId, "MEMBER");

    await prisma.checklistItemAssignee.upsert({
      where: {
        checklistItemId_userId: { checklistItemId: req.params.id, userId: req.params.userId },
      },
      update: {},
      create: { checklistItemId: req.params.id, userId: req.params.userId },
    });

    const actor = await prisma.user.findUnique({ where: { id: req.userId! } });
    await notifyUser({
      recipientId: req.params.userId,
      actorId: req.userId!,
      type: "ASSIGNMENT",
      message: `${actor?.name ?? "Someone"} assigned you a checklist item on "${item.checklist.card.title}"`,
      cardId: item.checklist.cardId,
      boardId,
    });

    await respondWithCard(item.checklist.cardId, boardId, res);
  } catch (err) {
    next(err);
  }
});

checklistItemsRouter.delete("/:id/assignees/:userId", async (req, res, next) => {
  try {
    const item = await loadItemWithCard(req.params.id);
    const boardId = item.checklist.card.boardId;
    await requireBoardAccess(req.userId!, boardId, "MEMBER");

    await prisma.checklistItemAssignee.delete({
      where: {
        checklistItemId_userId: { checklistItemId: req.params.id, userId: req.params.userId },
      },
    });

    await respondWithCard(item.checklist.cardId, boardId, res);
  } catch (err) {
    next(err);
  }
});
