import { prisma } from '@/lib/prisma'
import { sendStyledNotificationEmail } from '@/lib/email'

const REMINDER_TYPES = [
  { type: '3_DAYS', thresholdMs: 3 * 24 * 60 * 60 * 1000 },
  { type: '24H', thresholdMs: 24 * 60 * 60 * 1000 },
  { type: '1H', thresholdMs: 60 * 60 * 1000 },
] as const

type ReminderType = (typeof REMINDER_TYPES)[number]['type']

interface ReminderResult {
  sent: number
  errors: number
}

/**
 * Manually send reminders to all jurors with incomplete assignments for a round.
 * Bypasses window/deadline checks — the admin explicitly chose to send now.
 * Uses 'MANUAL' type so it doesn't interfere with automated cron deduplication,
 * but still deduplicates within manual sends (one manual reminder per juror per round).
 */
export async function sendManualReminders(roundId: string): Promise<ReminderResult> {
  let sent = 0
  let errors = 0

  const round = await prisma.round.findUnique({
    where: { id: roundId },
    select: {
      id: true,
      name: true,
      windowCloseAt: true,
      competition: { select: { name: true } },
    },
  })

  if (!round) return { sent, errors }

  // Find jurors with incomplete assignments
  const incompleteAssignments = await prisma.assignment.findMany({
    where: { roundId, isCompleted: false },
    select: { userId: true },
  })

  const userIds = [...new Set(incompleteAssignments.map((a) => a.userId))]
  if (userIds.length === 0) return { sent, errors }

  // Deduplicate: only one MANUAL reminder per juror per round
  const existingManual = await prisma.reminderLog.findMany({
    where: { roundId, type: 'MANUAL', userId: { in: userIds } },
    select: { userId: true },
  })
  const alreadySent = new Set(existingManual.map((r) => r.userId))
  const usersToNotify = userIds.filter((id) => !alreadySent.has(id))

  if (usersToNotify.length === 0) return { sent, errors }

  const users = await prisma.user.findMany({
    where: { id: { in: usersToNotify } },
    select: { id: true, name: true, email: true },
  })

  const baseUrl = process.env.NEXTAUTH_URL || 'https://portal.monaco-opc.com'
  const deadlineStr = round.windowCloseAt
    ? round.windowCloseAt.toLocaleDateString('en-US', {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        hour: '2-digit',
        minute: '2-digit',
        timeZoneName: 'short',
      })
    : undefined

  const pendingCounts = new Map<string, number>()
  for (const a of incompleteAssignments) {
    pendingCounts.set(a.userId, (pendingCounts.get(a.userId) || 0) + 1)
  }

  for (const user of users) {
    const pendingCount = pendingCounts.get(user.id) || 0
    if (pendingCount === 0) continue

    try {
      const deadlineNote = deadlineStr ? ` The deadline is ${deadlineStr}.` : ''
      await sendStyledNotificationEmail(
        user.email,
        user.name || '',
        'REMINDER_24H',
        {
          name: user.name || undefined,
          title: `Evaluation Reminder - ${round.name}`,
          message: `You have ${pendingCount} pending evaluation${pendingCount !== 1 ? 's' : ''} for ${round.name}.${deadlineNote}`,
          linkUrl: `${baseUrl}/jury/rounds/${round.id}/assignments`,
          metadata: {
            pendingCount,
            roundName: round.name,
            ...(deadlineStr && { deadline: deadlineStr }),
          },
        }
      )

      await prisma.reminderLog.create({
        data: { roundId, userId: user.id, type: 'MANUAL' },
      })

      sent++
    } catch (error) {
      console.error(
        `Failed to send manual reminder to ${user.email} for round ${round.name}:`,
        error
      )
      errors++
    }
  }

  return { sent, errors }
}

/**
 * Find active stages with approaching deadlines and send reminders
 * to jurors who have incomplete assignments. (Used by cron job)
 */
export async function processEvaluationReminders(roundId?: string): Promise<ReminderResult> {
  const now = new Date()
  let totalSent = 0
  let totalErrors = 0

  // Find active rounds with window close dates in the future
  const rounds = await prisma.round.findMany({
    where: {
      status: 'ROUND_ACTIVE' as const,
      windowCloseAt: { gt: now },
      windowOpenAt: { lte: now },
      ...(roundId && { id: roundId }),
    },
    select: {
      id: true,
      name: true,
      windowCloseAt: true,
      competition: { select: { name: true } },
    },
  })

  for (const round of rounds) {
    if (!round.windowCloseAt) continue

    const msUntilDeadline = round.windowCloseAt.getTime() - now.getTime()

    // Determine which reminder types should fire for this round
    const applicableTypes = REMINDER_TYPES.filter(
      ({ thresholdMs }) => msUntilDeadline <= thresholdMs
    )

    if (applicableTypes.length === 0) continue

    for (const { type } of applicableTypes) {
      const result = await sendRemindersForRound(round, type, now)
      totalSent += result.sent
      totalErrors += result.errors
    }
  }

  return { sent: totalSent, errors: totalErrors }
}

async function sendRemindersForRound(
  round: {
    id: string
    name: string
    windowCloseAt: Date | null
    competition: { name: string } | null
  },
  type: ReminderType,
  now: Date
): Promise<ReminderResult> {
  let sent = 0
  let errors = 0

  if (!round.windowCloseAt) return { sent, errors }

  // Find jurors with incomplete assignments for this round
  const incompleteAssignments = await prisma.assignment.findMany({
    where: {
      roundId: round.id,
      isCompleted: false,
    },
    select: {
      userId: true,
    },
  })

  // Get unique user IDs with incomplete work
  const userIds = [...new Set(incompleteAssignments.map((a) => a.userId))]

  if (userIds.length === 0) return { sent, errors }

  // Check which users already received this reminder type for this round
  const existingReminders = await prisma.reminderLog.findMany({
    where: {
      roundId: round.id,
      type,
      userId: { in: userIds },
    },
    select: { userId: true },
  })

  const alreadySent = new Set(existingReminders.map((r) => r.userId))
  const usersToNotify = userIds.filter((id) => !alreadySent.has(id))

  if (usersToNotify.length === 0) return { sent, errors }

  // Get user details and their pending counts
  const users = await prisma.user.findMany({
    where: { id: { in: usersToNotify } },
    select: { id: true, name: true, email: true },
  })

  const baseUrl = process.env.NEXTAUTH_URL || 'https://portal.monaco-opc.com'
  const deadlineStr = round.windowCloseAt.toLocaleDateString('en-US', {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    timeZoneName: 'short',
  })

  // Map to get pending count per user
  const pendingCounts = new Map<string, number>()
  for (const a of incompleteAssignments) {
    pendingCounts.set(a.userId, (pendingCounts.get(a.userId) || 0) + 1)
  }

  // Select email template type based on reminder type
  const emailTemplateType = type === '1H' ? 'REMINDER_1H' : type === '3_DAYS' ? 'REMINDER_3_DAYS' : 'REMINDER_24H'

  for (const user of users) {
    const pendingCount = pendingCounts.get(user.id) || 0
    if (pendingCount === 0) continue

    try {
      await sendStyledNotificationEmail(
        user.email,
        user.name || '',
        emailTemplateType,
        {
          name: user.name || undefined,
          title: `Evaluation Reminder - ${round.name}`,
          message: `You have ${pendingCount} pending evaluation${pendingCount !== 1 ? 's' : ''} for ${round.name}.`,
          linkUrl: `${baseUrl}/jury/rounds/${round.id}/assignments`,
          metadata: {
            pendingCount,
            roundName: round.name,
            deadline: deadlineStr,
          },
        }
      )

      // Log the sent reminder
      await prisma.reminderLog.create({
        data: {
          roundId: round.id,
          userId: user.id,
          type,
        },
      })

      sent++
    } catch (error) {
      console.error(
        `Failed to send ${type} reminder to ${user.email} for round ${round.name}:`,
        error
      )
      errors++
    }
  }

  return { sent, errors }
}
