import { TRPCError } from '@trpc/server'
import type {
  Competition,
  Round,
  JuryGroup,
  JuryGroupMember,
  SubmissionWindow,
  AssignmentIntent,
  Program,
  User,
  RoundType,
} from '@prisma/client'
import { prisma } from '@/lib/prisma'
import { validateRoundConfig, type RoundConfigMap } from '@/types/competition-configs'

// ============================================================================
// Context Types
// ============================================================================

export type CompetitionContext = {
  competition: Competition & { program: Program }
  round: Round
  roundConfig: RoundConfigMap[RoundType]
  juryGroup: JuryGroup | null
  submissionWindows: SubmissionWindow[]
}

export type MemberContext = CompetitionContext & {
  member: JuryGroupMember
  user: Pick<User, 'id' | 'name' | 'email' | 'role'>
  currentAssignmentCount: number
  assignmentsByCategory: Record<string, number>
  pendingIntents: AssignmentIntent[]
}

// ============================================================================
// resolveCompetitionContext
// ============================================================================

/**
 * Load full competition context for a given round.
 * Parses the round's configJson into a typed config using the round's RoundType.
 */
export async function resolveCompetitionContext(
  roundId: string,
): Promise<CompetitionContext> {
  const round = await prisma.round.findUnique({
    where: { id: roundId },
    include: {
      competition: {
        include: { program: true },
      },
      juryGroup: true,
    },
  })

  if (!round) {
    throw new TRPCError({ code: 'NOT_FOUND', message: `Round ${roundId} not found` })
  }

  // Parse the typed config
  const roundConfig = validateRoundConfig(
    round.roundType,
    round.configJson as Record<string, unknown>,
  )

  // Load submission windows for the competition
  const submissionWindows = await prisma.submissionWindow.findMany({
    where: { competitionId: round.competitionId },
    orderBy: { roundNumber: 'asc' },
  })

  return {
    competition: round.competition,
    round,
    roundConfig,
    juryGroup: round.juryGroup,
    submissionWindows,
  }
}

// ============================================================================
// resolveMemberContext
// ============================================================================

/**
 * Load full member context for a given round + user.
 * Includes assignment counts, category breakdown, and pending intents.
 */
export async function resolveMemberContext(
  roundId: string,
  userId: string,
): Promise<MemberContext> {
  const ctx = await resolveCompetitionContext(roundId)

  if (!ctx.juryGroup) {
    throw new TRPCError({
      code: 'BAD_REQUEST',
      message: `Round ${roundId} has no linked jury group`,
    })
  }

  // Find the member in this jury group
  const member = await prisma.juryGroupMember.findUnique({
    where: {
      juryGroupId_userId: {
        juryGroupId: ctx.juryGroup.id,
        userId,
      },
    },
    include: {
      user: { select: { id: true, name: true, email: true, role: true } },
    },
  })

  if (!member) {
    throw new TRPCError({
      code: 'NOT_FOUND',
      message: `User ${userId} is not a member of jury group "${ctx.juryGroup.name}"`,
    })
  }

  // Count current assignments for this user in this round
  const assignments = await prisma.assignment.findMany({
    where: {
      userId,
      roundId,
    },
    include: {
      project: { select: { competitionCategory: true } },
    },
  })

  const currentAssignmentCount = assignments.length

  // Break down assignments by category
  const assignmentsByCategory: Record<string, number> = {}
  for (const a of assignments) {
    const cat = a.project.competitionCategory ?? 'UNCATEGORIZED'
    assignmentsByCategory[cat] = (assignmentsByCategory[cat] ?? 0) + 1
  }

  // Load pending intents
  const pendingIntents = await prisma.assignmentIntent.findMany({
    where: {
      juryGroupMemberId: member.id,
      roundId,
      status: 'INTENT_PENDING',
    },
  })

  return {
    ...ctx,
    member,
    user: member.user,
    currentAssignmentCount,
    assignmentsByCategory,
    pendingIntents,
  }
}
