import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { router, protectedProcedure, adminProcedure } from '../trpc'

export const evaluationRouter = router({
  /**
   * Get evaluation for an assignment
   */
  get: protectedProcedure
    .input(z.object({ assignmentId: z.string() }))
    .query(async ({ ctx, input }) => {
      // Verify ownership or admin
      const assignment = await ctx.prisma.assignment.findUniqueOrThrow({
        where: { id: input.assignmentId },
        include: { round: true },
      })

      if (
        ctx.user.role === 'JURY_MEMBER' &&
        assignment.userId !== ctx.user.id
      ) {
        throw new TRPCError({ code: 'FORBIDDEN' })
      }

      return ctx.prisma.evaluation.findUnique({
        where: { assignmentId: input.assignmentId },
        include: {
          form: true,
        },
      })
    }),

  /**
   * Start an evaluation (creates draft)
   */
  start: protectedProcedure
    .input(
      z.object({
        assignmentId: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify assignment ownership
      const assignment = await ctx.prisma.assignment.findUniqueOrThrow({
        where: { id: input.assignmentId },
        include: {
          round: {
            include: {
              evaluationForms: { where: { isActive: true }, take: 1 },
            },
          },
        },
      })

      if (assignment.userId !== ctx.user.id) {
        throw new TRPCError({ code: 'FORBIDDEN' })
      }

      // Get active form
      const form = assignment.round.evaluationForms[0]
      if (!form) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'No active evaluation form for this round',
        })
      }

      // Check if evaluation exists
      const existing = await ctx.prisma.evaluation.findUnique({
        where: { assignmentId: input.assignmentId },
      })

      if (existing) return existing

      return ctx.prisma.evaluation.create({
        data: {
          assignmentId: input.assignmentId,
          formId: form.id,
          status: 'DRAFT',
        },
      })
    }),

  /**
   * Autosave evaluation (debounced on client)
   */
  autosave: protectedProcedure
    .input(
      z.object({
        id: z.string(),
        criterionScoresJson: z.record(z.number()).optional(),
        globalScore: z.number().int().min(1).max(10).optional().nullable(),
        binaryDecision: z.boolean().optional().nullable(),
        feedbackText: z.string().optional().nullable(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input

      // Verify ownership and status
      const evaluation = await ctx.prisma.evaluation.findUniqueOrThrow({
        where: { id },
        include: { assignment: true },
      })

      if (evaluation.assignment.userId !== ctx.user.id) {
        throw new TRPCError({ code: 'FORBIDDEN' })
      }

      if (
        evaluation.status === 'SUBMITTED' ||
        evaluation.status === 'LOCKED'
      ) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Cannot edit submitted evaluation',
        })
      }

      return ctx.prisma.evaluation.update({
        where: { id },
        data: {
          ...data,
          status: 'DRAFT',
        },
      })
    }),

  /**
   * Submit evaluation (final)
   */
  submit: protectedProcedure
    .input(
      z.object({
        id: z.string(),
        criterionScoresJson: z.record(z.number()),
        globalScore: z.number().int().min(1).max(10),
        binaryDecision: z.boolean(),
        feedbackText: z.string().min(10),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input

      // Verify ownership
      const evaluation = await ctx.prisma.evaluation.findUniqueOrThrow({
        where: { id },
        include: {
          assignment: {
            include: { round: true },
          },
        },
      })

      if (evaluation.assignment.userId !== ctx.user.id) {
        throw new TRPCError({ code: 'FORBIDDEN' })
      }

      // Check voting window
      const round = evaluation.assignment.round
      const now = new Date()

      if (round.status !== 'ACTIVE') {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Round is not active',
        })
      }

      // Check for grace period
      const gracePeriod = await ctx.prisma.gracePeriod.findFirst({
        where: {
          roundId: round.id,
          userId: ctx.user.id,
          OR: [
            { projectId: null },
            { projectId: evaluation.assignment.projectId },
          ],
          extendedUntil: { gte: now },
        },
      })

      const effectiveEndDate = gracePeriod?.extendedUntil ?? round.votingEndAt

      if (round.votingStartAt && now < round.votingStartAt) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Voting has not started yet',
        })
      }

      if (effectiveEndDate && now > effectiveEndDate) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Voting window has closed',
        })
      }

      // Submit
      const updated = await ctx.prisma.evaluation.update({
        where: { id },
        data: {
          ...data,
          status: 'SUBMITTED',
          submittedAt: now,
        },
      })

      // Mark assignment as completed
      await ctx.prisma.assignment.update({
        where: { id: evaluation.assignmentId },
        data: { isCompleted: true },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'EVALUATION_SUBMITTED',
          entityType: 'Evaluation',
          entityId: id,
          detailsJson: {
            projectId: evaluation.assignment.projectId,
            roundId: evaluation.assignment.roundId,
            globalScore: data.globalScore,
            binaryDecision: data.binaryDecision,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return updated
    }),

  /**
   * Get aggregated stats for a project (admin only)
   */
  getProjectStats: adminProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => {
      const evaluations = await ctx.prisma.evaluation.findMany({
        where: {
          status: 'SUBMITTED',
          assignment: { projectId: input.projectId },
        },
      })

      if (evaluations.length === 0) {
        return null
      }

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

      const yesVotes = evaluations.filter(
        (e) => e.binaryDecision === true
      ).length

      return {
        totalEvaluations: evaluations.length,
        averageGlobalScore:
          globalScores.length > 0
            ? globalScores.reduce((a, b) => a + b, 0) / globalScores.length
            : null,
        minScore: globalScores.length > 0 ? Math.min(...globalScores) : null,
        maxScore: globalScores.length > 0 ? Math.max(...globalScores) : null,
        yesVotes,
        noVotes: evaluations.length - yesVotes,
        yesPercentage: (yesVotes / evaluations.length) * 100,
      }
    }),

  /**
   * Get all evaluations for a round (admin only)
   */
  listByRound: adminProcedure
    .input(
      z.object({
        roundId: z.string(),
        status: z.enum(['NOT_STARTED', 'DRAFT', 'SUBMITTED', 'LOCKED']).optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      return ctx.prisma.evaluation.findMany({
        where: {
          assignment: { roundId: input.roundId },
          ...(input.status && { status: input.status }),
        },
        include: {
          assignment: {
            include: {
              user: { select: { id: true, name: true, email: true } },
              project: { select: { id: true, title: true } },
            },
          },
        },
        orderBy: { updatedAt: 'desc' },
      })
    }),

  /**
   * Get my past evaluations (read-only for jury)
   */
  myPastEvaluations: protectedProcedure
    .input(z.object({ roundId: z.string().optional() }))
    .query(async ({ ctx, input }) => {
      return ctx.prisma.evaluation.findMany({
        where: {
          assignment: {
            userId: ctx.user.id,
            ...(input.roundId && { roundId: input.roundId }),
          },
          status: 'SUBMITTED',
        },
        include: {
          assignment: {
            include: {
              project: { select: { id: true, title: true } },
              round: { select: { id: true, name: true } },
            },
          },
        },
        orderBy: { submittedAt: 'desc' },
      })
    }),
})
