/**
 * Task 5: Confirmation reminder cron
 *
 * Tests that sendDueConfirmationReminders:
 * - sends a FINALIST_REMINDER notification for leads whose deadline is within the window
 * - stamps reminderSentAt so the second call is idempotent
 * - skips rows whose deadline is further away than reminderHoursBeforeDeadline
 */
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { prisma } from '../setup'
import {
  createTestProgram,
  createTestCompetition,
  createTestRound,
  createTestProject,
  cleanupTestData,
  uid,
} from '../helpers'
import { sendDueConfirmationReminders } from '../../src/server/services/finalist-confirmation'

beforeAll(() => {
  process.env.NEXTAUTH_SECRET = 'test-secret-for-finalist-tokens'
  process.env.NEXTAUTH_URL = 'http://localhost:3001'
})

describe('sendDueConfirmationReminders', () => {
  const programIds: string[] = []
  const userIds: string[] = []

  afterAll(async () => {
    for (const programId of programIds) {
      await prisma.inAppNotification.deleteMany({
        where: { metadata: { path: ['projectId'], string_contains: '' } },
      })
      await prisma.finalistConfirmation.deleteMany({ where: { project: { programId } } })
      await cleanupTestData(programId, [])
    }
    if (userIds.length > 0) {
      await prisma.user.deleteMany({ where: { id: { in: userIds } } })
    }
  })

  it('sends FINALIST_REMINDER for a lead whose deadline is within the reminder window', async () => {
    const program = await createTestProgram({ name: `reminder-due-${uid()}` })
    programIds.push(program.id)
    const competition = await createTestCompetition(program.id, { status: 'ACTIVE' })
    // LIVE_FINAL round with 12h reminder window
    await createTestRound(competition.id, {
      roundType: 'LIVE_FINAL',
      configJson: { reminderHoursBeforeDeadline: 12 },
    })

    const lead = await prisma.user.create({
      data: {
        id: uid('user'),
        email: `lead-reminder-${uid()}@test.local`,
        name: 'Reminder Lead',
        role: 'APPLICANT',
        roles: ['APPLICANT'],
        status: 'ACTIVE',
      },
    })
    userIds.push(lead.id)

    const project = await createTestProject(program.id, {
      title: 'Reminder Project',
      competitionCategory: 'STARTUP',
    })
    await prisma.teamMember.create({
      data: { projectId: project.id, userId: lead.id, role: 'LEAD' },
    })

    // Deadline 6 hours from now — within the 12h window
    const deadline = new Date(Date.now() + 6 * 3_600_000)
    const token = `tok_reminder_${uid()}`
    await prisma.finalistConfirmation.create({
      data: {
        projectId: project.id,
        category: 'STARTUP',
        status: 'PENDING',
        deadline,
        token,
        reminderSentAt: null,
      },
    })

    const result = await sendDueConfirmationReminders(prisma)
    expect(result.remindersSent).toBe(1)

    // Notification created for the lead
    const notification = await prisma.inAppNotification.findFirst({
      where: { userId: lead.id, type: 'FINALIST_REMINDER' },
    })
    expect(notification).not.toBeNull()
    expect(notification?.metadata).toMatchObject({ projectTitle: 'Reminder Project' })

    // reminderSentAt is stamped
    const updated = await prisma.finalistConfirmation.findUniqueOrThrow({
      where: { projectId: project.id },
    })
    expect(updated.reminderSentAt).not.toBeNull()

    // Clean up notification so it doesn't interfere with idempotency test
    await prisma.inAppNotification.deleteMany({ where: { userId: lead.id, type: 'FINALIST_REMINDER' } })
  })

  it('is idempotent — second call sends 0 reminders for the same row', async () => {
    // Reuse the row created above — reminderSentAt is now set
    const program = await createTestProgram({ name: `reminder-idempotent-${uid()}` })
    programIds.push(program.id)
    const competition = await createTestCompetition(program.id, { status: 'ACTIVE' })
    await createTestRound(competition.id, {
      roundType: 'LIVE_FINAL',
      configJson: { reminderHoursBeforeDeadline: 12 },
    })

    const lead = await prisma.user.create({
      data: {
        id: uid('user'),
        email: `lead-idempotent-${uid()}@test.local`,
        name: 'Idempotent Lead',
        role: 'APPLICANT',
        roles: ['APPLICANT'],
        status: 'ACTIVE',
      },
    })
    userIds.push(lead.id)

    const project = await createTestProject(program.id, {
      title: 'Idempotent Project',
      competitionCategory: 'STARTUP',
    })
    await prisma.teamMember.create({
      data: { projectId: project.id, userId: lead.id, role: 'LEAD' },
    })

    const deadline = new Date(Date.now() + 6 * 3_600_000)
    const token = `tok_idempotent_${uid()}`
    await prisma.finalistConfirmation.create({
      data: {
        projectId: project.id,
        category: 'STARTUP',
        status: 'PENDING',
        deadline,
        token,
        reminderSentAt: null,
      },
    })

    // First call — should send 1
    const first = await sendDueConfirmationReminders(prisma)
    expect(first.remindersSent).toBeGreaterThanOrEqual(1)

    // Second call — same row, reminderSentAt is now set → 0
    const second = await sendDueConfirmationReminders(prisma)
    expect(second.remindersSent).toBe(0)
  })

  it('does NOT send for a PENDING row whose deadline is outside the reminder window (48h from now, 12h window)', async () => {
    const program = await createTestProgram({ name: `reminder-notdue-${uid()}` })
    programIds.push(program.id)
    const competition = await createTestCompetition(program.id, { status: 'ACTIVE' })
    await createTestRound(competition.id, {
      roundType: 'LIVE_FINAL',
      configJson: { reminderHoursBeforeDeadline: 12 },
    })

    const lead = await prisma.user.create({
      data: {
        id: uid('user'),
        email: `lead-notdue-${uid()}@test.local`,
        name: 'Not-Due Lead',
        role: 'APPLICANT',
        roles: ['APPLICANT'],
        status: 'ACTIVE',
      },
    })
    userIds.push(lead.id)

    const project = await createTestProject(program.id, {
      title: 'Not Due Project',
      competitionCategory: 'STARTUP',
    })
    await prisma.teamMember.create({
      data: { projectId: project.id, userId: lead.id, role: 'LEAD' },
    })

    // Deadline 48 hours from now — far outside the 12h window
    const deadline = new Date(Date.now() + 48 * 3_600_000)
    const token = `tok_notdue_${uid()}`
    await prisma.finalistConfirmation.create({
      data: {
        projectId: project.id,
        category: 'STARTUP',
        status: 'PENDING',
        deadline,
        token,
        reminderSentAt: null,
      },
    })

    const result = await sendDueConfirmationReminders(prisma)
    // The only unflagged row in this program has deadline 48h out, should not be sent
    const notification = await prisma.inAppNotification.findFirst({
      where: { userId: lead.id, type: 'FINALIST_REMINDER' },
    })
    expect(notification).toBeNull()
    // reminderSentAt still null
    const row = await prisma.finalistConfirmation.findUniqueOrThrow({
      where: { projectId: project.id },
    })
    expect(row.reminderSentAt).toBeNull()
    void result // result.remindersSent may be > 0 from other programs' rows already in DB
  })
})
