import { afterAll, describe, expect, it } from 'vitest'
import { prisma, createCaller } from '../setup'
import {
  createTestUser,
  createTestProgram,
  createTestCompetition,
  createTestRound,
  cleanupTestData,
  uid,
} from '../helpers'
import { programRouter } from '../../src/server/routers/program'

describe('program.getEditionSettings', () => {
  const programIds: string[] = []
  const userIds: string[] = []

  afterAll(async () => {
    for (const programId of programIds) {
      await cleanupTestData(programId, [])
    }
    if (userIds.length > 0) {
      await prisma.user.deleteMany({ where: { id: { in: userIds } } })
    }
  })

  it('returns the merged view (program fields + LIVE_FINAL round config)', async () => {
    const admin = await createTestUser('SUPER_ADMIN')
    userIds.push(admin.id)
    const program = await createTestProgram({
      name: `edition-merged-${uid()}`,
      defaultAttendeeCap: 5,
    })
    programIds.push(program.id)
    await prisma.program.update({
      where: { id: program.id },
      data: { visaStatusVisibleToMembers: false },
    })
    const competition = await createTestCompetition(program.id)
    await createTestRound(competition.id, {
      roundType: 'LIVE_FINAL',
      sortOrder: 99,
      configJson: { attendeeEditCutoffHours: 24, confirmationWindowHours: 12 },
    })

    const caller = createCaller(programRouter, {
      id: admin.id,
      email: admin.email,
      role: 'SUPER_ADMIN',
    })
    const result = await caller.getEditionSettings({ programId: program.id })
    expect(result.defaultAttendeeCap).toBe(5)
    expect(result.visaStatusVisibleToMembers).toBe(false)
    expect(result.attendeeEditCutoffHours).toBe(24)
    expect(result.confirmationWindowHours).toBe(12)
    expect(result.liveFinalRoundId).not.toBeNull()
  })

  it('falls back to defaults when LIVE_FINAL round has no relevant config', async () => {
    const admin = await createTestUser('SUPER_ADMIN')
    userIds.push(admin.id)
    const program = await createTestProgram({ name: `edition-defaults-${uid()}` })
    programIds.push(program.id)
    const competition = await createTestCompetition(program.id)
    await createTestRound(competition.id, {
      roundType: 'LIVE_FINAL',
      sortOrder: 99,
      configJson: {},
    })

    const caller = createCaller(programRouter, {
      id: admin.id,
      email: admin.email,
      role: 'SUPER_ADMIN',
    })
    const result = await caller.getEditionSettings({ programId: program.id })
    expect(result.attendeeEditCutoffHours).toBe(48)
    expect(result.confirmationWindowHours).toBe(24)
  })

  it('returns nulls for round-derived fields when no LIVE_FINAL round exists', async () => {
    const admin = await createTestUser('SUPER_ADMIN')
    userIds.push(admin.id)
    const program = await createTestProgram({ name: `edition-noround-${uid()}` })
    programIds.push(program.id)

    const caller = createCaller(programRouter, {
      id: admin.id,
      email: admin.email,
      role: 'SUPER_ADMIN',
    })
    const result = await caller.getEditionSettings({ programId: program.id })
    expect(result.attendeeEditCutoffHours).toBeNull()
    expect(result.confirmationWindowHours).toBeNull()
    expect(result.liveFinalRoundId).toBeNull()
  })
})

describe('program.updateEditionSettings', () => {
  const programIds: string[] = []
  const userIds: string[] = []

  afterAll(async () => {
    for (const programId of programIds) {
      await cleanupTestData(programId, [])
    }
    if (userIds.length > 0) {
      await prisma.user.deleteMany({ where: { id: { in: userIds } } })
    }
  })

  it('writes program fields + round configJson + audit-logs', async () => {
    const admin = await createTestUser('SUPER_ADMIN')
    userIds.push(admin.id)
    const program = await createTestProgram({ name: `edition-update-${uid()}` })
    programIds.push(program.id)
    const competition = await createTestCompetition(program.id)
    const round = await createTestRound(competition.id, {
      roundType: 'LIVE_FINAL',
      sortOrder: 99,
    })

    const caller = createCaller(programRouter, {
      id: admin.id,
      email: admin.email,
      role: 'SUPER_ADMIN',
    })
    await caller.updateEditionSettings({
      programId: program.id,
      defaultAttendeeCap: 4,
      visaStatusVisibleToMembers: false,
      attendeeEditCutoffHours: 36,
      confirmationWindowHours: 18,
    })

    const refreshed = await prisma.program.findUniqueOrThrow({ where: { id: program.id } })
    expect(refreshed.defaultAttendeeCap).toBe(4)
    expect(refreshed.visaStatusVisibleToMembers).toBe(false)

    const updatedRound = await prisma.round.findUniqueOrThrow({ where: { id: round.id } })
    const cfg = updatedRound.configJson as Record<string, unknown>
    expect(cfg.attendeeEditCutoffHours).toBe(36)
    expect(cfg.confirmationWindowHours).toBe(18)

    const audit = await prisma.auditLog.findFirst({
      where: { action: 'PROGRAM_EDITION_SETTINGS_UPDATE', entityId: program.id },
    })
    expect(audit).not.toBeNull()
  })

  it('preserves untouched configJson keys', async () => {
    const admin = await createTestUser('SUPER_ADMIN')
    userIds.push(admin.id)
    const program = await createTestProgram({ name: `edition-preserve-${uid()}` })
    programIds.push(program.id)
    const competition = await createTestCompetition(program.id)
    const round = await createTestRound(competition.id, {
      roundType: 'LIVE_FINAL',
      sortOrder: 99,
      configJson: { foo: 'bar', attendeeEditCutoffHours: 48 },
    })

    const caller = createCaller(programRouter, {
      id: admin.id,
      email: admin.email,
      role: 'SUPER_ADMIN',
    })
    await caller.updateEditionSettings({
      programId: program.id,
      attendeeEditCutoffHours: 24,
    })

    const updated = await prisma.round.findUniqueOrThrow({ where: { id: round.id } })
    const cfg = updated.configJson as Record<string, unknown>
    expect(cfg.foo).toBe('bar')
    expect(cfg.attendeeEditCutoffHours).toBe(24)
  })
})
