import { Router } from "express";
import { z } from "zod";
import { requireBoardAccess } from "../lib/authz";
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 listsRouter = Router();
listsRouter.use(requireAuth);

const createListSchema = z.object({
  boardId: z.string().min(1),
  name: z.string().min(1).max(100),
});

listsRouter.post("/", async (req, res, next) => {
  try {
    const data = createListSchema.parse(req.body);
    await requireBoardAccess(req.userId!, data.boardId, "MEMBER");

    const last = await prisma.list.findFirst({
      where: { boardId: data.boardId, isArchived: false },
      orderBy: { position: "desc" },
    });

    const list = await prisma.list.create({
      data: {
        boardId: data.boardId,
        name: data.name,
        position: positionBetween(last?.position, undefined),
      },
    });

    emitToBoard(data.boardId, "board:changed", {});
    res.status(201).json({ list });
  } catch (err) {
    next(err);
  }
});

const updateListSchema = z.object({
  name: z.string().min(1).max(100).optional(),
  wipLimit: z.number().int().positive().nullable().optional(),
  cardLimit: z.number().int().positive().nullable().optional(),
  isArchived: z.boolean().optional(),
  beforePosition: z.number().optional(),
  afterPosition: z.number().optional(),
});

listsRouter.patch("/:id", async (req, res, next) => {
  try {
    const list = await prisma.list.findUnique({ where: { id: req.params.id } });
    if (!list) throw new HttpError(404, "List not found");
    await requireBoardAccess(req.userId!, list.boardId, "MEMBER");

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

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

    emitToBoard(list.boardId, "board:changed", {});
    res.json({ list: updated });
  } catch (err) {
    next(err);
  }
});

listsRouter.delete("/:id", async (req, res, next) => {
  try {
    const list = await prisma.list.findUnique({ where: { id: req.params.id } });
    if (!list) throw new HttpError(404, "List not found");
    await requireBoardAccess(req.userId!, list.boardId, "ADMIN");

    await prisma.list.delete({ where: { id: req.params.id } });
    emitToBoard(list.boardId, "board:changed", {});
    res.status(204).send();
  } catch (err) {
    next(err);
  }
});

listsRouter.post("/:id/sort-cards", async (req, res, next) => {
  try {
    const list = await prisma.list.findUnique({ where: { id: req.params.id } });
    if (!list) throw new HttpError(404, "List not found");
    await requireBoardAccess(req.userId!, list.boardId, "MEMBER");

    const sortBySchema = z.object({ sortBy: z.enum(["dueDate", "title", "member"]) });
    const { sortBy } = sortBySchema.parse(req.body);

    const cards = await prisma.card.findMany({
      where: { listId: list.id, isArchived: false },
      include: { members: { include: { user: true } } },
    });

    const sorted = [...cards].sort((a, b) => {
      if (sortBy === "title") return a.title.localeCompare(b.title);
      if (sortBy === "dueDate") {
        if (!a.dueDate && !b.dueDate) return 0;
        if (!a.dueDate) return 1;
        if (!b.dueDate) return -1;
        return a.dueDate.getTime() - b.dueDate.getTime();
      }
      const nameA = a.members[0]?.user.name ?? "";
      const nameB = b.members[0]?.user.name ?? "";
      return nameA.localeCompare(nameB);
    });

    await prisma.$transaction(
      sorted.map((card, index) =>
        prisma.card.update({ where: { id: card.id }, data: { position: (index + 1) * 1000 } })
      )
    );

    emitToBoard(list.boardId, "board:changed", {});
    res.status(200).json({ message: "Sorted" });
  } catch (err) {
    next(err);
  }
});
