import type { CompetitionCategory, Prisma, PrismaClient } from '@prisma/client'
import { signFinalistToken } from '@/lib/finalist-token'
import { ensureLunchPickForAttendingMember } from './lunch-pick-sync'

type TxClient = PrismaClient | Prisma.TransactionClient

/**
 * Re-invite-safe variant of createPendingConfirmation. If a confirmation row
 * already exists for the project (projectId is @unique), reset any
 * non-CONFIRMED row back to a fresh PENDING with a new token/deadline and
 * clear stale attendee rows; report CONFIRMED rows as a no-op so callers can
 * skip them. Returns the row id + token + deadline for the email step.
 */
export async function resetOrCreatePendingConfirmation(
  prisma: TxClient,
  args: { projectId: string; category: CompetitionCategory; windowHours: number },
): Promise<{ id: string; token: string; deadline: Date; alreadyConfirmed: boolean }> {
  const deadline = new Date(Date.now() + args.windowHours * 3_600_000)
  const existing = await prisma.finalistConfirmation.findUnique({
    where: { projectId: args.projectId },
    select: { id: true, status: true },
  })

  if (existing?.status === 'CONFIRMED') {
    return { id: existing.id, token: '', deadline, alreadyConfirmed: true }
  }

  if (existing) {
    const token = signFinalistToken({
      confirmationId: existing.id,
      exp: Math.floor(deadline.getTime() / 1000),
    })
    // Clear any attendee rows from a prior cycle (cascade-deletes flight/visa/lunch).
    await prisma.attendingMember.deleteMany({ where: { confirmationId: existing.id } })
    await prisma.finalistConfirmation.update({
      where: { id: existing.id },
      data: {
        category: args.category,
        status: 'PENDING',
        deadline,
        token,
        confirmedAt: null,
        declinedAt: null,
        declineReason: null,
        expiredAt: null,
        reminderSentAt: null,
      },
    })
    return { id: existing.id, token, deadline, alreadyConfirmed: false }
  }

  const id = `cmfc_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`
  const token = signFinalistToken({
    confirmationId: id,
    exp: Math.floor(deadline.getTime() / 1000),
  })
  await prisma.finalistConfirmation.create({
    data: {
      id,
      projectId: args.projectId,
      category: args.category,
      status: 'PENDING',
      deadline,
      token,
    },
  })
  return { id, token, deadline, alreadyConfirmed: false }
}

/**
 * Shared confirm transaction: atomically writes CONFIRMED status + attendee
 * rows + visa applications + lunch picks.
 * Called from both `adminConfirm` and `enrollFinalists` (ADMIN_CONFIRM mode).
 *
 * Must be called inside a Prisma $transaction block — `tx` is the transaction
 * client, NOT the top-level prisma client.
 */
export async function confirmAttendanceInTx(
  tx: Prisma.TransactionClient,
  args: {
    confirmationId: string
    attendingUserIds: string[]
    visaFlags: Record<string, boolean>
  },
): Promise<void> {
  await tx.finalistConfirmation.update({
    where: { id: args.confirmationId },
    data: { status: 'CONFIRMED', confirmedAt: new Date() },
  })
  await tx.attendingMember.createMany({
    data: args.attendingUserIds.map((userId) => ({
      confirmationId: args.confirmationId,
      userId,
      needsVisa: args.visaFlags[userId] ?? false,
    })),
  })
  const visaUsers = args.attendingUserIds.filter((uid) => args.visaFlags[uid] === true)
  if (visaUsers.length > 0) {
    const created = await tx.attendingMember.findMany({
      where: { confirmationId: args.confirmationId, userId: { in: visaUsers } },
      select: { id: true },
    })
    await tx.visaApplication.createMany({
      data: created.map((m) => ({ attendingMemberId: m.id, status: 'REQUESTED' })),
    })
  }
  const allMembers = await tx.attendingMember.findMany({
    where: { confirmationId: args.confirmationId, userId: { in: args.attendingUserIds } },
    select: { id: true },
  })
  for (const m of allMembers) {
    await ensureLunchPickForAttendingMember(tx, m.id)
  }
}
