import { z } from 'zod'
import { router, protectedProcedure, adminProcedure, observerProcedure } from '../trpc'

export const analyticsRouter = router({
  /**
   * Get score distribution for a round (histogram data)
   */
  getScoreDistribution: observerProcedure
    .input(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: {
          assignment: { roundId: input.roundId },
          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(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: {
          assignment: { roundId: input.roundId },
          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(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const assignments = await ctx.prisma.assignment.findMany({
        where: { roundId: input.roundId },
        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(z.object({ roundId: z.string(), limit: z.number().optional() }))
    .query(async ({ ctx, input }) => {
      const projects = await ctx.prisma.project.findMany({
        where: { roundId: input.roundId },
        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(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const projects = await ctx.prisma.project.groupBy({
        by: ['status'],
        where: { roundId: input.roundId },
        _count: true,
      })

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

  /**
   * Get overview stats for dashboard
   */
  getOverviewStats: observerProcedure
    .input(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const [
        projectCount,
        assignmentCount,
        evaluationCount,
        jurorCount,
        statusCounts,
      ] = await Promise.all([
        ctx.prisma.project.count({ where: { roundId: input.roundId } }),
        ctx.prisma.assignment.count({ where: { roundId: input.roundId } }),
        ctx.prisma.evaluation.count({
          where: {
            assignment: { roundId: input.roundId },
            status: 'SUBMITTED',
          },
        }),
        ctx.prisma.assignment.groupBy({
          by: ['userId'],
          where: { roundId: input.roundId },
        }),
        ctx.prisma.project.groupBy({
          by: ['status'],
          where: { roundId: input.roundId },
          _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(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      // Get active evaluation form for this round
      const evaluationForm = await ctx.prisma.evaluationForm.findFirst({
        where: { roundId: input.roundId, isActive: true },
      })

      if (!evaluationForm?.criteriaJson) {
        return []
      }

      // Parse criteria from JSON
      const criteria = evaluationForm.criteriaJson as Array<{
        id: string
        label: string
      }>

      if (!criteria || criteria.length === 0) {
        return []
      }

      // Get all evaluations
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: {
          assignment: { roundId: input.roundId },
          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
        ? { roundId: input.roundId }
        : { round: { programId: input.programId } }

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

      return distribution.map((d) => ({
        countryCode: d.country || 'UNKNOWN',
        count: d._count.id,
      }))
    }),

  // =========================================================================
  // 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: { 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

          // Get average scores
          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

          // Score distribution
          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(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: {
          assignment: { roundId: input.roundId },
          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(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      const projects = await ctx.prisma.project.findMany({
        where: { roundId: input.roundId },
        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 rounds = await ctx.prisma.round.findMany({
        where: { programId: input.programId },
        select: { id: true, name: true, createdAt: true },
        orderBy: { createdAt: 'asc' },
      })

      const stats = await Promise.all(
        rounds.map(async (round) => {
          const [projectCount, evaluationCount, assignmentCount] = await Promise.all([
            ctx.prisma.project.count({ where: { 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

          // Average score
          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
    }),
})
