import { eq, and } from "drizzle-orm"; import * as schema from "~/database/schema"; import { sendEmail, wrapInEmailTemplate, emailParagraph, emailButton, escapeHtml } from "~/lib/email.server"; import { calculatePickInfo } from "~/models/draft-utils"; import { logger } from "~/lib/logger"; import type { database } from "~/database/context"; type DraftSlot = { teamId: string; draftOrder: number }; export async function sendOnTheClockEmail(params: { seasonId: string; currentPickNumber: number; totalTeams: number; totalPicks: number; draftSlots: DraftSlot[]; db: ReturnType; }): Promise { const { seasonId, currentPickNumber, totalTeams, totalPicks, draftSlots, db } = params; if (currentPickNumber > totalPicks) return; const { pickInRound } = calculatePickInfo(currentPickNumber, totalTeams); const currentSlot = draftSlots.find((s) => s.draftOrder === pickInRound); if (!currentSlot) return; // Fetch team + owner user const team = await db.query.teams.findFirst({ where: eq(schema.teams.id, currentSlot.teamId), }); if (!team?.ownerId) return; const owner = await db.query.users.findFirst({ where: eq(schema.users.id, team.ownerId as string), }); if (!owner?.email || !owner.draftEmailNotificationsEnabled) return; // Safety net: skip if this team has autodraft enabled (their pick will be made automatically) const autodraft = await db.query.autodraftSettings.findFirst({ where: and( eq(schema.autodraftSettings.seasonId, seasonId), eq(schema.autodraftSettings.teamId, currentSlot.teamId) ), }); if (autodraft?.isEnabled) return; // Detect consecutive picks (same team picks the very next slot too) let isConsecutive = false; if (currentPickNumber + 1 <= totalPicks) { const { pickInRound: nextPickInRound } = calculatePickInfo(currentPickNumber + 1, totalTeams); const nextSlot = draftSlots.find((s) => s.draftOrder === nextPickInRound); if (nextSlot?.teamId === currentSlot.teamId) { isConsecutive = true; } } // Fetch season + league for context const season = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId), }); if (!season) return; const league = await db.query.leagues.findFirst({ where: eq(schema.leagues.id, season.leagueId), }); if (!league) return; const appUrl = process.env.APP_URL ?? "https://brackt.com"; const draftUrl = `${appUrl}/leagues/${season.leagueId}/draft/${seasonId}`; const leagueName = escapeHtml(league.name); const teamName = escapeHtml(team.name); const { round } = calculatePickInfo(currentPickNumber, totalTeams); let subject: string; let preheader: string; let bodyHtml: string; if (isConsecutive) { const { round: round2 } = calculatePickInfo(currentPickNumber + 1, totalTeams); subject = `You have back-to-back picks in ${league.name}`; preheader = `${team.name} picks twice in a row — head to the draft room!`; bodyHtml = ` ${emailParagraph(`You're up twice in a row!`)} ${emailParagraph(`${teamName} has back-to-back picks (Round ${round} and Round ${round2}) in ${leagueName}. Head to the draft room to make both picks before time runs out.`)} ${emailButton(draftUrl, "Go to Draft Room")} ${emailParagraph(`You're receiving this because you enabled draft email notifications in your account settings.`)} `; } else { subject = `You're on the clock in ${league.name}`; preheader = `${team.name} is on the clock — make your pick!`; bodyHtml = ` ${emailParagraph(`You're on the clock!`)} ${emailParagraph(`${teamName} is now on the clock in Round ${round} of ${leagueName}. Head to the draft room to make your pick before time runs out.`)} ${emailButton(draftUrl, "Go to Draft Room")} ${emailParagraph(`You're receiving this because you enabled draft email notifications in your account settings.`)} `; } const { error } = await sendEmail({ to: owner.email, subject, html: wrapInEmailTemplate(bodyHtml, preheader), }); if (error) { logger.error(`[DraftEmail] Failed to send on-the-clock email to ${owner.email}:`, error); } }