import { getAuth } from "@clerk/react-router/server"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq, and, gte } from "drizzle-orm"; import { isCommissioner } from "~/models/commissioner"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; import type { ActionFunctionArgs } from "react-router"; export async function action(args: ActionFunctionArgs) { const { request } = args; const { userId } = await getAuth(args); if (!userId) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } const formData = await request.formData(); const seasonId = formData.get("seasonId") as string; const pickNumber = parseInt(formData.get("pickNumber") as string); if (!seasonId || isNaN(pickNumber) || pickNumber < 1) { return Response.json({ error: "Missing required fields" }, { status: 400 }); } const db = database(); const season = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId), }); if (!season) { return Response.json({ error: "Season not found" }, { status: 404 }); } if (!(await isCommissioner(season.leagueId, userId))) { return Response.json({ error: "Only commissioners can roll back the draft" }, { status: 403 }); } if (season.status !== "draft") { return Response.json( { error: "Cannot roll back a draft that is not in progress" }, { status: 400 } ); } const draftSlots = await db.query.draftSlots.findMany({ where: eq(schema.draftSlots.seasonId, seasonId), orderBy: schema.draftSlots.draftOrder, }); const totalTeams = draftSlots.length; if (totalTeams === 0) { return Response.json({ error: "No draft slots found for this season" }, { status: 400 }); } // Calculate which team is on the clock at the rollback pick (snake draft) const round = Math.ceil(pickNumber / totalTeams); const isEvenRound = round % 2 === 0; let pickInRound = ((pickNumber - 1) % totalTeams) + 1; if (isEvenRound) { pickInRound = totalTeams - pickInRound + 1; } const rollbackSlot = draftSlots.find((slot) => slot.draftOrder === pickInRound); // Delete picks from pickNumber onwards await db .delete(schema.draftPicks) .where( and( eq(schema.draftPicks.seasonId, seasonId), gte(schema.draftPicks.pickNumber, pickNumber) ) ); // Update season: reset pick number and unpause await db .update(schema.seasons) .set({ currentPickNumber: pickNumber, draftPaused: false, }) .where(eq(schema.seasons.id, seasonId)); // Emit socket events try { const io = getSocketIO(); io.to(`draft-${seasonId}`).emit("draft-rolled-back", { seasonId, pickNumber, teamId: rollbackSlot?.teamId, }); } catch (error) { logger.error("Socket.IO error:", error); } return Response.json({ success: true, pickNumber }); }