import { z } from 'zod'
import { router, observerProcedure } from '../trpc'
import { normalizeCountryToCode } from '@/lib/countries'

const editionOrRoundInput = z.object({
  roundId: z.string().optional(),
  programId: z.string().optional(),
}).refine(data => data.roundId || data.programId, {
  message: 'Either roundId or programId is required',
})

function projectWhere(input: { roundId?: string; programId?: string }) {
  if (input.roundId) return { assignments: { some: { roundId: input.roundId } } }
  return { programId: input.programId! }
}

function assignmentWhere(input: { roundId?: string; programId?: string }) {
  if (input.roundId) return { roundId: input.roundId }
  return { round: { competition: { programId: input.programId! } } }
}

function evalWhere(input: { roundId?: string; programId?: string }, extra: Record<string, unknown> = {}) {
  const base = input.roundId
    ? { assignment: { roundId: input.roundId } }
    : { assignment: { round: { competition: { programId: input.programId! } } } }
  return { ...base, ...extra }
}

export const analyticsRouter = router({
  /**
   * Get score distribution (histogram data)
   */
  getScoreDistribution: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: evalWhere(input, { status: 'SUBMITTED' }),
        select: {
          criterionScoresJson: true,
        },
      })

      // Extract all scores and calculate distribution
      const allScores: number[] = []
      evaluations.forEach((evaluation) => {
        const scores = evaluation.criterionScoresJson as Record<string, number> | null
        if (scores) {
          Object.values(scores).forEach((score) => {
            if (typeof score === 'number') {
              allScores.push(score)
            }
          })
        }
      })

      // Count scores by bucket (1-10)
      const distribution = Array.from({ length: 10 }, (_, i) => ({
        score: i + 1,
        count: allScores.filter((s) => Math.round(s) === i + 1).length,
      }))

      return {
        distribution,
        totalScores: allScores.length,
        averageScore:
          allScores.length > 0
            ? allScores.reduce((a, b) => a + b, 0) / allScores.length
            : 0,
      }
    }),

  /**
   * Get evaluation completion over time (timeline data)
   */
  getEvaluationTimeline: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: evalWhere(input, { status: 'SUBMITTED' }),
        select: {
          submittedAt: true,
        },
        orderBy: { submittedAt: 'asc' },
      })

      // Group by date
      const byDate: Record<string, number> = {}
      let cumulative = 0

      evaluations.forEach((evaluation) => {
        if (evaluation.submittedAt) {
          const date = evaluation.submittedAt.toISOString().split('T')[0]
          if (!byDate[date]) {
            byDate[date] = 0
          }
          byDate[date]++
        }
      })

      // Convert to cumulative timeline
      const timeline = Object.entries(byDate)
        .sort(([a], [b]) => a.localeCompare(b))
        .map(([date, count]) => {
          cumulative += count
          return {
            date,
            daily: count,
            cumulative,
          }
        })

      return timeline
    }),

  /**
   * Get juror workload distribution
   */
  getJurorWorkload: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const assignments = await ctx.prisma.assignment.findMany({
        where: assignmentWhere(input),
        include: {
          user: { select: { name: true, email: true } },
          evaluation: {
            select: { id: true, status: true },
          },
        },
      })

      // Group by user
      const byUser: Record<
        string,
        { name: string; assigned: number; completed: number }
      > = {}

      assignments.forEach((assignment) => {
        const userId = assignment.userId
        if (!byUser[userId]) {
          byUser[userId] = {
            name: assignment.user.name || assignment.user.email || 'Unknown',
            assigned: 0,
            completed: 0,
          }
        }
        byUser[userId].assigned++
        if (assignment.evaluation?.status === 'SUBMITTED') {
          byUser[userId].completed++
        }
      })

      return Object.entries(byUser)
        .map(([id, data]) => ({
          id,
          ...data,
          completionRate:
            data.assigned > 0
              ? Math.round((data.completed / data.assigned) * 100)
              : 0,
        }))
        .sort((a, b) => b.assigned - a.assigned)
    }),

  /**
   * Get project rankings with average scores
   */
  getProjectRankings: observerProcedure
    .input(editionOrRoundInput.and(z.object({ limit: z.number().optional() })))
    .query(async ({ ctx, input }) => {
      const projects = await ctx.prisma.project.findMany({
        where: projectWhere(input),
        select: {
          id: true,
          title: true,
          teamName: true,
          status: true,
          assignments: {
            select: {
              evaluation: {
                select: { criterionScoresJson: true, status: true },
              },
            },
          },
        },
      })

      // Calculate average scores
      const rankings = projects
        .map((project) => {
          const allScores: number[] = []

          project.assignments.forEach((assignment) => {
            const evaluation = assignment.evaluation
            if (evaluation?.status === 'SUBMITTED') {
              const scores = evaluation.criterionScoresJson as Record<
                string,
                number
              > | null
              if (scores) {
                const scoreValues = Object.values(scores).filter(
                  (s): s is number => typeof s === 'number'
                )
                if (scoreValues.length > 0) {
                  const average =
                    scoreValues.reduce((a, b) => a + b, 0) / scoreValues.length
                  allScores.push(average)
                }
              }
            }
          })

          const averageScore =
            allScores.length > 0
              ? allScores.reduce((a, b) => a + b, 0) / allScores.length
              : null

          return {
            id: project.id,
            title: project.title,
            teamName: project.teamName,
            status: project.status,
            averageScore,
            evaluationCount: allScores.length,
          }
        })
        .filter((p) => p.averageScore !== null)
        .sort((a, b) => (b.averageScore || 0) - (a.averageScore || 0))

      return input.limit ? rankings.slice(0, input.limit) : rankings
    }),

  /**
   * Get status breakdown (pie chart data)
   */
  getStatusBreakdown: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const projects = await ctx.prisma.project.groupBy({
        by: ['status'],
        where: projectWhere(input),
        _count: true,
      })

      return projects.map((p) => ({
        status: p.status,
        count: p._count,
      }))
    }),

  /**
   * Get overview stats for dashboard
   */
  getOverviewStats: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const [
        projectCount,
        assignmentCount,
        evaluationCount,
        jurorCount,
        statusCounts,
      ] = await Promise.all([
        ctx.prisma.project.count({ where: projectWhere(input) }),
        ctx.prisma.assignment.count({ where: assignmentWhere(input) }),
        ctx.prisma.evaluation.count({
          where: evalWhere(input, { status: 'SUBMITTED' }),
        }),
        ctx.prisma.assignment.groupBy({
          by: ['userId'],
          where: assignmentWhere(input),
        }),
        ctx.prisma.project.groupBy({
          by: ['status'],
          where: projectWhere(input),
          _count: true,
        }),
      ])

      const completionRate =
        assignmentCount > 0
          ? Math.round((evaluationCount / assignmentCount) * 100)
          : 0

      return {
        projectCount,
        assignmentCount,
        evaluationCount,
        jurorCount: jurorCount.length,
        completionRate,
        statusBreakdown: statusCounts.map((s) => ({
          status: s.status,
          count: s._count,
        })),
      }
    }),

  /**
   * Get criteria-level score distribution
   */
  getCriteriaScores: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const formWhere = input.roundId
        ? { roundId: input.roundId, isActive: true }
        : { round: { competition: { programId: input.programId! } }, isActive: true }

      const evaluationForms = await ctx.prisma.evaluationForm.findMany({
        where: formWhere,
      })

      if (!evaluationForms.length) {
        return []
      }

      const criteriaMap = new Map<string, { id: string; label: string }>()
      evaluationForms.forEach((form) => {
        const criteria = form.criteriaJson as Array<{ id: string; label: string }> | null
        if (criteria) {
          criteria.forEach((c) => {
            const key = input.roundId ? c.id : c.label
            if (!criteriaMap.has(key)) {
              criteriaMap.set(key, c)
            }
          })
        }
      })

      const criteria = Array.from(criteriaMap.values())
      if (criteria.length === 0) {
        return []
      }

      // Get all evaluations
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: evalWhere(input, { status: 'SUBMITTED' }),
        select: { criterionScoresJson: true },
      })

      // Calculate average score per criterion
      const criteriaScores = criteria.map((criterion) => {
        const scores: number[] = []

        evaluations.forEach((evaluation) => {
          const criterionScoresJson = evaluation.criterionScoresJson as Record<
            string,
            number
          > | null
          if (criterionScoresJson && typeof criterionScoresJson[criterion.id] === 'number') {
            scores.push(criterionScoresJson[criterion.id])
          }
        })

        return {
          id: criterion.id,
          name: criterion.label,
          averageScore:
            scores.length > 0
              ? scores.reduce((a, b) => a + b, 0) / scores.length
              : 0,
          count: scores.length,
        }
      })

      return criteriaScores
    }),

  /**
   * Get geographic distribution of projects by country
   */
  getGeographicDistribution: observerProcedure
    .input(
      z.object({
        programId: z.string(),
        roundId: z.string().optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      const where = input.roundId
        ? { assignments: { some: { roundId: input.roundId } } }
        : { programId: input.programId }

      const distribution = await ctx.prisma.project.groupBy({
        by: ['country'],
        where: { ...where, country: { not: null } },
        _count: { id: true },
      })

      // Resolve country names to ISO codes (DB may store "France" instead of "FR")
      const codeMap = new Map<string, number>()
      for (const d of distribution) {
        const resolved = normalizeCountryToCode(d.country) ?? d.country ?? 'UNKNOWN'
        codeMap.set(resolved, (codeMap.get(resolved) ?? 0) + d._count.id)
      }

      return Array.from(codeMap.entries()).map(([countryCode, count]) => ({
        countryCode,
        count,
      }))
    }),

  // =========================================================================
  // Advanced Analytics (F10)
  // =========================================================================

  /**
   * Compare metrics across multiple rounds
   */
  getCrossRoundComparison: observerProcedure
    .input(z.object({ roundIds: z.array(z.string()).min(2) }))
    .query(async ({ ctx, input }) => {
      const comparisons = await Promise.all(
        input.roundIds.map(async (roundId) => {
          const round = await ctx.prisma.round.findUniqueOrThrow({
            where: { id: roundId },
            select: { id: true, name: true },
          })

          const [projectCount, assignmentCount, evaluationCount] = await Promise.all([
            ctx.prisma.project.count({
              where: { assignments: { some: { roundId } } },
            }),
            ctx.prisma.assignment.count({ where: { roundId } }),
            ctx.prisma.evaluation.count({
              where: {
                assignment: { roundId },
                status: 'SUBMITTED',
              },
            }),
          ])

          const completionRate = assignmentCount > 0
            ? Math.round((evaluationCount / assignmentCount) * 100)
            : 0

          const evaluations = await ctx.prisma.evaluation.findMany({
            where: {
              assignment: { roundId },
              status: 'SUBMITTED',
            },
            select: { globalScore: true },
          })

          const globalScores = evaluations
            .map((e) => e.globalScore)
            .filter((s): s is number => s !== null)

          const averageScore = globalScores.length > 0
            ? globalScores.reduce((a, b) => a + b, 0) / globalScores.length
            : null

          const distribution = Array.from({ length: 10 }, (_, i) => ({
            score: i + 1,
            count: globalScores.filter((s) => Math.round(s) === i + 1).length,
          }))

          return {
            roundId,
            roundName: round.name,
            projectCount,
            evaluationCount,
            completionRate,
            averageScore,
            scoreDistribution: distribution,
          }
        })
      )

      return comparisons
    }),

  /**
   * Get juror consistency metrics for a round
   */
  getJurorConsistency: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: evalWhere(input, { status: 'SUBMITTED' }),
        include: {
          assignment: {
            include: {
              user: { select: { id: true, name: true, email: true } },
            },
          },
        },
      })

      // Group scores by juror
      const jurorScores: Record<string, { name: string; email: string; scores: number[] }> = {}

      evaluations.forEach((e) => {
        const userId = e.assignment.userId
        if (!jurorScores[userId]) {
          jurorScores[userId] = {
            name: e.assignment.user.name || e.assignment.user.email || 'Unknown',
            email: e.assignment.user.email || '',
            scores: [],
          }
        }
        if (e.globalScore !== null) {
          jurorScores[userId].scores.push(e.globalScore)
        }
      })

      // Calculate overall average
      const allScores = Object.values(jurorScores).flatMap((j) => j.scores)
      const overallAverage = allScores.length > 0
        ? allScores.reduce((a, b) => a + b, 0) / allScores.length
        : 0

      // Calculate per-juror metrics
      const metrics = Object.entries(jurorScores).map(([userId, data]) => {
        const avg = data.scores.length > 0
          ? data.scores.reduce((a, b) => a + b, 0) / data.scores.length
          : 0
        const variance = data.scores.length > 1
          ? data.scores.reduce((sum, s) => sum + Math.pow(s - avg, 2), 0) / data.scores.length
          : 0
        const stddev = Math.sqrt(variance)
        const deviationFromOverall = Math.abs(avg - overallAverage)

        return {
          userId,
          name: data.name,
          email: data.email,
          evaluationCount: data.scores.length,
          averageScore: avg,
          stddev,
          deviationFromOverall,
          isOutlier: deviationFromOverall > 2, // Flag if 2+ points from mean
        }
      })

      return {
        overallAverage,
        jurors: metrics.sort((a, b) => b.deviationFromOverall - a.deviationFromOverall),
      }
    }),

  /**
   * Get diversity metrics for projects in a round
   */
  getDiversityMetrics: observerProcedure
    .input(editionOrRoundInput)
    .query(async ({ ctx, input }) => {
      const projects = await ctx.prisma.project.findMany({
        where: projectWhere(input),
        select: {
          country: true,
          competitionCategory: true,
          oceanIssue: true,
          tags: true,
        },
      })

      const total = projects.length
      if (total === 0) {
        return { total: 0, byCountry: [], byCategory: [], byOceanIssue: [], byTag: [] }
      }

      // By country
      const countryCounts: Record<string, number> = {}
      projects.forEach((p) => {
        const key = p.country || 'Unknown'
        countryCounts[key] = (countryCounts[key] || 0) + 1
      })
      const byCountry = Object.entries(countryCounts)
        .map(([country, count]) => ({ country, count, percentage: (count / total) * 100 }))
        .sort((a, b) => b.count - a.count)

      // By competition category
      const categoryCounts: Record<string, number> = {}
      projects.forEach((p) => {
        const key = p.competitionCategory || 'Uncategorized'
        categoryCounts[key] = (categoryCounts[key] || 0) + 1
      })
      const byCategory = Object.entries(categoryCounts)
        .map(([category, count]) => ({ category, count, percentage: (count / total) * 100 }))
        .sort((a, b) => b.count - a.count)

      // By ocean issue
      const issueCounts: Record<string, number> = {}
      projects.forEach((p) => {
        const key = p.oceanIssue || 'Unspecified'
        issueCounts[key] = (issueCounts[key] || 0) + 1
      })
      const byOceanIssue = Object.entries(issueCounts)
        .map(([issue, count]) => ({ issue, count, percentage: (count / total) * 100 }))
        .sort((a, b) => b.count - a.count)

      // By tag
      const tagCounts: Record<string, number> = {}
      projects.forEach((p) => {
        p.tags.forEach((tag) => {
          tagCounts[tag] = (tagCounts[tag] || 0) + 1
        })
      })
      const byTag = Object.entries(tagCounts)
        .map(([tag, count]) => ({ tag, count, percentage: (count / total) * 100 }))
        .sort((a, b) => b.count - a.count)

      return { total, byCountry, byCategory, byOceanIssue, byTag }
    }),

  /**
   * Get year-over-year stats across all rounds in a program
   */
  getYearOverYear: observerProcedure
    .input(z.object({ programId: z.string() }))
    .query(async ({ ctx, input }) => {
      const competitions = await ctx.prisma.competition.findMany({
        where: { programId: input.programId },
        include: {
          rounds: {
            select: { id: true, name: true, createdAt: true },
            orderBy: { createdAt: 'asc' },
          },
        },
        orderBy: { createdAt: 'asc' },
      })

      const allRounds = competitions.flatMap((c) => c.rounds)

      const stats = await Promise.all(
        allRounds.map(async (round) => {
          const [projectCount, evaluationCount, assignmentCount] = await Promise.all([
            ctx.prisma.project.count({
              where: { assignments: { some: { roundId: round.id } } },
            }),
            ctx.prisma.evaluation.count({
              where: {
                assignment: { roundId: round.id },
                status: 'SUBMITTED',
              },
            }),
            ctx.prisma.assignment.count({ where: { roundId: round.id } }),
          ])

          const completionRate = assignmentCount > 0
            ? Math.round((evaluationCount / assignmentCount) * 100)
            : 0

          const evaluations = await ctx.prisma.evaluation.findMany({
            where: {
              assignment: { roundId: round.id },
              status: 'SUBMITTED',
            },
            select: { globalScore: true },
          })

          const scores = evaluations
            .map((e) => e.globalScore)
            .filter((s): s is number => s !== null)

          const averageScore = scores.length > 0
            ? scores.reduce((a, b) => a + b, 0) / scores.length
            : null

          return {
            roundId: round.id,
            roundName: round.name,
            createdAt: round.createdAt,
            projectCount,
            evaluationCount,
            completionRate,
            averageScore,
          }
        })
      )

      return stats
    }),

  /**
   * Get dashboard stats (optionally scoped to a round)
   */
  getDashboardStats: observerProcedure
    .input(z.object({ roundId: z.string().optional() }).optional())
    .query(async ({ ctx, input }) => {
      const roundId = input?.roundId

      const projectFilter = roundId
        ? { assignments: { some: { roundId } } }
        : {}
      const assignmentFilter = roundId ? { roundId } : {}
      const evalFilter = roundId
        ? { assignment: { roundId }, status: 'SUBMITTED' as const }
        : { status: 'SUBMITTED' as const }

      const [
        programCount,
        activeRoundCount,
        projectCount,
        jurorCount,
        submittedEvaluations,
        totalAssignments,
        evaluationScores,
      ] = await Promise.all([
        ctx.prisma.program.count(),
        ctx.prisma.round.count({ where: { status: 'ROUND_ACTIVE' } }),
        ctx.prisma.project.count({ where: projectFilter }),
        ctx.prisma.user.count({ where: { role: 'JURY_MEMBER', status: 'ACTIVE' } }),
        ctx.prisma.evaluation.count({ where: evalFilter }),
        ctx.prisma.assignment.count({ where: assignmentFilter }),
        ctx.prisma.evaluation.findMany({
          where: { ...evalFilter, globalScore: { not: null } },
          select: { globalScore: true },
        }),
      ])

      const completionRate = totalAssignments > 0
        ? Math.round((submittedEvaluations / totalAssignments) * 100)
        : 0

      const scores = evaluationScores.map((e) => e.globalScore!).filter((s) => s != null)
      const scoreDistribution = [
        { label: '9-10', min: 9, max: 10 },
        { label: '7-8', min: 7, max: 8.99 },
        { label: '5-6', min: 5, max: 6.99 },
        { label: '3-4', min: 3, max: 4.99 },
        { label: '1-2', min: 1, max: 2.99 },
      ].map((b) => ({
        label: b.label,
        count: scores.filter((s) => s >= b.min && s <= b.max).length,
      }))

      return {
        programCount,
        activeRoundCount,
        projectCount,
        jurorCount,
        submittedEvaluations,
        totalEvaluations: totalAssignments,
        completionRate,
        scoreDistribution,
      }
    }),

  // =========================================================================
  // Stage-Scoped Analytics (Phase 4)
  // =========================================================================

  /**
   * Get score distribution histogram for round evaluations
   */
  getRoundScoreDistribution: observerProcedure
    .input(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: {
          status: 'SUBMITTED',
          assignment: { roundId: input.roundId },
        },
        select: {
          globalScore: true,
          criterionScoresJson: true,
        },
      })

      // Global score distribution (1-10 buckets)
      const globalScores = evaluations
        .map((e) => e.globalScore)
        .filter((s): s is number => s !== null)

      const globalDistribution = Array.from({ length: 10 }, (_, i) => ({
        score: i + 1,
        count: globalScores.filter((s) => Math.round(s) === i + 1).length,
      }))

      // Per-criterion score distribution
      const criterionScores: Record<string, number[]> = {}
      evaluations.forEach((e) => {
        const scores = e.criterionScoresJson as Record<string, number> | null
        if (scores) {
          Object.entries(scores).forEach(([key, value]) => {
            if (typeof value === 'number') {
              if (!criterionScores[key]) criterionScores[key] = []
              criterionScores[key].push(value)
            }
          })
        }
      })

      const criterionDistributions = Object.entries(criterionScores).map(([criterionId, scores]) => ({
        criterionId,
        average: scores.reduce((a, b) => a + b, 0) / scores.length,
        count: scores.length,
        distribution: Array.from({ length: 10 }, (_, i) => ({
          score: i + 1,
          count: scores.filter((s) => Math.round(s) === i + 1).length,
        })),
      }))

      return {
        globalDistribution,
        totalEvaluations: evaluations.length,
        averageGlobalScore:
          globalScores.length > 0
            ? globalScores.reduce((a, b) => a + b, 0) / globalScores.length
            : 0,
        criterionDistributions,
      }
    }),

  /**
   * Get per-round completion summary for a competition
   * NOTE: This replaces the old pipeline-based getStageCompletionOverview
   */
  getRoundCompletionOverview: observerProcedure
    .input(z.object({ competitionId: z.string() }))
    .query(async ({ ctx, input }) => {
      // Get all rounds in the competition
      const rounds = await ctx.prisma.round.findMany({
        where: { competitionId: input.competitionId },
        orderBy: { sortOrder: 'asc' },
        select: {
          id: true,
          name: true,
          roundType: true,
          status: true,
          sortOrder: true,
        },
      })

      // For each round, get assignment coverage and evaluation completion
      const roundOverviews = await Promise.all(
        rounds.map(async (round) => {
          const [
            projectRoundStates,
            totalAssignments,
            completedEvaluations,
            distinctJurors,
          ] = await Promise.all([
            ctx.prisma.projectRoundState.groupBy({
              by: ['state'],
              where: { roundId: round.id },
              _count: true,
            }),
            ctx.prisma.assignment.count({
              where: { roundId: round.id },
            }),
            ctx.prisma.evaluation.count({
              where: {
                assignment: { roundId: round.id },
                status: 'SUBMITTED',
              },
            }),
            ctx.prisma.assignment.groupBy({
              by: ['userId'],
              where: { roundId: round.id },
            }),
          ])

          const stateBreakdown = projectRoundStates.map((ps) => ({
            state: ps.state,
            count: ps._count,
          }))

          const totalProjects = projectRoundStates.reduce((sum, ps) => sum + ps._count, 0)
          const completionRate = totalAssignments > 0
            ? Math.round((completedEvaluations / totalAssignments) * 100)
            : 0

          return {
            roundId: round.id,
            roundName: round.name,
            roundType: round.roundType,
            roundStatus: round.status,
            sortOrder: round.sortOrder,
            totalProjects,
            stateBreakdown,
            totalAssignments,
            completedEvaluations,
            pendingEvaluations: totalAssignments - completedEvaluations,
            completionRate,
            jurorCount: distinctJurors.length,
          }
        })
      )

      return {
        competitionId: input.competitionId,
        rounds: roundOverviews,
        summary: {
          totalRounds: rounds.length,
          totalProjects: roundOverviews.reduce((sum, s) => sum + s.totalProjects, 0),
          totalAssignments: roundOverviews.reduce((sum, s) => sum + s.totalAssignments, 0),
          totalCompleted: roundOverviews.reduce((sum, s) => sum + s.completedEvaluations, 0),
        },
      }
    }),

  // =========================================================================
  // Award Analytics (Phase 5)
  // =========================================================================

  // NOTE: getAwardSummary procedure removed - depends on deleted Pipeline/Track/Stage/SpecialAward models
  // Will need to be reimplemented with new Competition/Round/Award architecture

  // NOTE: getAwardVoteDistribution procedure removed - depends on deleted Stage/Track/SpecialAward/AwardVote models
  // Will need to be reimplemented with new Competition/Round/Award architecture

  /**
   * Get all projects with pagination, filtering, and search (for observer dashboard)
   */
  getAllProjects: observerProcedure
    .input(
      z.object({
        roundId: z.string().optional(),
        search: z.string().optional(),
        status: z.string().optional(),
        page: z.number().min(1).default(1),
        perPage: z.number().min(1).max(100).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const where: Record<string, unknown> = {}

      if (input.roundId) {
        where.assignments = { some: { roundId: input.roundId } }
      }

      if (input.status) {
        where.status = input.status
      }

      if (input.search) {
        where.OR = [
          { title: { contains: input.search, mode: 'insensitive' } },
          { teamName: { contains: input.search, mode: 'insensitive' } },
        ]
      }

      const [projects, total] = await Promise.all([
        ctx.prisma.project.findMany({
          where,
          select: {
            id: true,
            title: true,
            teamName: true,
            status: true,
            country: true,
            assignments: {
              select: {
                roundId: true,
                round: { select: { id: true, name: true } },
                evaluation: {
                  select: { globalScore: true, status: true },
                },
              },
            },
          },
          orderBy: { title: 'asc' },
          skip: (input.page - 1) * input.perPage,
          take: input.perPage,
        }),
        ctx.prisma.project.count({ where }),
      ])

      const mapped = projects.map((p) => {
        const submitted = p.assignments
          .map((a) => a.evaluation)
          .filter((e) => e?.status === 'SUBMITTED')
        const scores = submitted
          .map((e) => e?.globalScore)
          .filter((s): s is number => s !== null)
        const averageScore =
          scores.length > 0
            ? scores.reduce((a, b) => a + b, 0) / scores.length
            : null

        const firstAssignment = p.assignments[0]

        return {
          id: p.id,
          title: p.title,
          teamName: p.teamName,
          status: p.status,
          country: p.country,
          roundId: firstAssignment?.round?.id ?? '',
          roundName: firstAssignment?.round?.name ?? '',
          averageScore,
          evaluationCount: submitted.length,
        }
      })

      return {
        projects: mapped,
        total,
        page: input.page,
        perPage: input.perPage,
        totalPages: Math.ceil(total / input.perPage),
      }
    }),
})
