brackt/app/routes/api/draft.force-autopick.ts

218 lines
6.2 KiB
TypeScript
Raw Normal View History

import { getAuth } from "@clerk/react-router/server";
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, notInArray, desc, asc, inArray } from "drizzle-orm";
export async function action(args: any) {
const { request } = args;
const auth = await getAuth(args);
const userId = (auth as any).userId as string | null;
if (!userId) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
const formData = await request.formData();
const seasonId = formData.get("seasonId") as string;
const teamId = formData.get("teamId") as string;
const pickNumber = parseInt(formData.get("pickNumber") as string);
if (!seasonId || !teamId || !pickNumber) {
return Response.json({ error: "Missing required fields" }, { status: 400 });
}
const db = database();
// Get season details
const season = await db.query.seasons.findFirst({
where: eq(schema.seasons.id, seasonId),
with: {
league: true,
},
});
if (!season) {
return Response.json({ error: "Season not found" }, { status: 404 });
}
// Check if user is commissioner
if (season.league.createdBy !== userId) {
return Response.json({ error: "Only commissioner can force autopick" }, { status: 403 });
}
// Get team's queue
const teamQueue = await db.query.draftQueue.findMany({
where: eq(schema.draftQueue.teamId, teamId),
orderBy: schema.draftQueue.queuePosition,
with: {
participant: {
with: {
sportsSeason: {
with: {
sport: true,
},
},
},
},
},
});
// Get already drafted participant IDs
const draftPicks = await db.query.draftPicks.findMany({
where: eq(schema.draftPicks.seasonId, seasonId),
});
const draftedParticipantIds = draftPicks.map((p) => p.participantId);
let participantToPick = null;
// 1. Try to pick from queue (first non-drafted participant)
for (const queueItem of teamQueue) {
if (!draftedParticipantIds.includes(queueItem.participantId)) {
participantToPick = queueItem.participant;
break;
}
}
// 2. If no valid queue item, pick highest EV available participant
if (!participantToPick) {
// Get sports seasons for this season
const seasonSports = await db.query.seasonSports.findMany({
where: eq(schema.seasonSports.seasonId, seasonId),
});
const sportsSeasonIds = seasonSports.map((ss) => ss.sportsSeasonId);
if (sportsSeasonIds.length > 0) {
const availableParticipants = await db
.select({
id: schema.participants.id,
name: schema.participants.name,
expectedValue: schema.participants.expectedValue,
sportsSeasonId: schema.participants.sportsSeasonId,
})
.from(schema.participants)
.where(
and(
sportsSeasonIds.length === 1
? eq(schema.participants.sportsSeasonId, sportsSeasonIds[0])
: inArray(schema.participants.sportsSeasonId, sportsSeasonIds),
draftedParticipantIds.length > 0
? notInArray(schema.participants.id, draftedParticipantIds)
: undefined
)
)
.orderBy(desc(schema.participants.expectedValue), asc(schema.participants.name))
.limit(1);
if (availableParticipants.length > 0) {
// Get full participant with sport info
participantToPick = await db.query.participants.findFirst({
where: eq(schema.participants.id, availableParticipants[0].id),
with: {
sportsSeason: {
with: {
sport: true,
},
},
},
});
}
}
}
if (!participantToPick) {
return Response.json({ error: "No available participants to pick" }, { status: 400 });
}
// Calculate round and pickInRound
const draftSlots = await db.query.draftSlots.findMany({
where: eq(schema.draftSlots.seasonId, seasonId),
orderBy: schema.draftSlots.draftOrder,
with: {
team: true,
},
});
const totalTeams = draftSlots.length;
const currentRound = Math.ceil(pickNumber / totalTeams);
const isEvenRound = currentRound % 2 === 0;
let pickInRound = ((pickNumber - 1) % totalTeams) + 1;
if (isEvenRound) {
pickInRound = totalTeams - pickInRound + 1;
}
// Create the draft pick
const [draftPick] = await db
.insert(schema.draftPicks)
.values({
seasonId,
teamId,
participantId: participantToPick.id,
pickNumber,
round: currentRound,
pickInRound,
pickedByUserId: userId,
pickedByType: "auto",
})
.returning();
// Update season's current pick number
const nextPickNumber = pickNumber + 1;
const totalPicks = totalTeams * season.draftRounds;
const isDraftComplete = nextPickNumber > totalPicks;
await db
.update(schema.seasons)
.set({
currentPickNumber: isDraftComplete ? pickNumber : nextPickNumber,
status: isDraftComplete ? "active" : season.status,
})
.where(eq(schema.seasons.id, seasonId));
// Remove from queue if it was picked from queue
if (teamQueue.some((item) => item.participantId === participantToPick.id)) {
await db
.delete(schema.draftQueue)
.where(
and(
eq(schema.draftQueue.teamId, teamId),
eq(schema.draftQueue.participantId, participantToPick.id)
)
);
}
// Emit socket event
try {
const io = (global as any).__socketIO;
const team = draftSlots.find((slot) => slot.team.id === teamId)?.team;
io.to(`draft-${seasonId}`).emit("pick-made", {
pick: {
...draftPick,
team,
participant: {
...participantToPick,
sport: participantToPick.sportsSeason.sport,
},
sport: participantToPick.sportsSeason.sport,
},
nextPickNumber: isDraftComplete ? pickNumber : nextPickNumber,
isDraftComplete,
});
if (isDraftComplete) {
io.to(`draft-${seasonId}`).emit("draft-completed");
}
} catch (error) {
console.error("Socket.IO error:", error);
}
return Response.json({
success: true,
pick: draftPick,
participant: participantToPick,
nextPickNumber: isDraftComplete ? pickNumber : nextPickNumber,
isDraftComplete,
});
}