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

export const liveVotingRouter = router({
  /**
   * Get or create a live voting session for a round
   */
  getSession: adminProcedure
    .input(z.object({ roundId: z.string() }))
    .query(async ({ ctx, input }) => {
      let session = await ctx.prisma.liveVotingSession.findUnique({
        where: { roundId: input.roundId },
        include: {
          round: {
            include: {
              program: { select: { name: true, year: true } },
              projects: {
                where: { status: { in: ['FINALIST', 'SEMIFINALIST'] } },
                select: { id: true, title: true, teamName: true },
              },
            },
          },
        },
      })

      if (!session) {
        // Create session
        session = await ctx.prisma.liveVotingSession.create({
          data: {
            roundId: input.roundId,
          },
          include: {
            round: {
              include: {
                program: { select: { name: true, year: true } },
                projects: {
                  where: { status: { in: ['FINALIST', 'SEMIFINALIST'] } },
                  select: { id: true, title: true, teamName: true },
                },
              },
            },
          },
        })
      }

      // Get current votes if voting is in progress
      let currentVotes: { userId: string; score: number }[] = []
      if (session.currentProjectId) {
        const votes = await ctx.prisma.liveVote.findMany({
          where: {
            sessionId: session.id,
            projectId: session.currentProjectId,
          },
          select: { userId: true, score: true },
        })
        currentVotes = votes
      }

      return {
        ...session,
        currentVotes,
      }
    }),

  /**
   * Get session for jury member voting
   */
  getSessionForVoting: protectedProcedure
    .input(z.object({ sessionId: z.string() }))
    .query(async ({ ctx, input }) => {
      const session = await ctx.prisma.liveVotingSession.findUniqueOrThrow({
        where: { id: input.sessionId },
        include: {
          round: {
            include: {
              program: { select: { name: true, year: true } },
            },
          },
        },
      })

      // Get current project if in progress
      let currentProject = null
      if (session.currentProjectId && session.status === 'IN_PROGRESS') {
        currentProject = await ctx.prisma.project.findUnique({
          where: { id: session.currentProjectId },
          select: { id: true, title: true, teamName: true, description: true },
        })
      }

      // Get user's vote for current project
      let userVote = null
      if (session.currentProjectId) {
        userVote = await ctx.prisma.liveVote.findFirst({
          where: {
            sessionId: session.id,
            projectId: session.currentProjectId,
            userId: ctx.user.id,
          },
        })
      }

      // Calculate time remaining
      let timeRemaining = null
      if (session.votingEndsAt && session.status === 'IN_PROGRESS') {
        const remaining = new Date(session.votingEndsAt).getTime() - Date.now()
        timeRemaining = Math.max(0, Math.floor(remaining / 1000))
      }

      return {
        session: {
          id: session.id,
          status: session.status,
          votingStartedAt: session.votingStartedAt,
          votingEndsAt: session.votingEndsAt,
        },
        round: session.round,
        currentProject,
        userVote,
        timeRemaining,
      }
    }),

  /**
   * Get public session info for display
   */
  getPublicSession: protectedProcedure
    .input(z.object({ sessionId: z.string() }))
    .query(async ({ ctx, input }) => {
      const session = await ctx.prisma.liveVotingSession.findUniqueOrThrow({
        where: { id: input.sessionId },
        include: {
          round: {
            include: {
              program: { select: { name: true, year: true } },
            },
          },
        },
      })

      // Get all projects in order
      const projectOrder = (session.projectOrderJson as string[]) || []
      const projects = await ctx.prisma.project.findMany({
        where: { id: { in: projectOrder } },
        select: { id: true, title: true, teamName: true },
      })

      // Sort by order
      const sortedProjects = projectOrder
        .map((id) => projects.find((p) => p.id === id))
        .filter(Boolean)

      // Get scores for each project
      const scores = await ctx.prisma.liveVote.groupBy({
        by: ['projectId'],
        where: { sessionId: session.id },
        _avg: { score: true },
        _count: true,
      })

      const projectsWithScores = sortedProjects.map((project) => {
        const projectScore = scores.find((s) => s.projectId === project!.id)
        return {
          ...project,
          averageScore: projectScore?._avg.score || null,
          voteCount: projectScore?._count || 0,
        }
      })

      return {
        session: {
          id: session.id,
          status: session.status,
          currentProjectId: session.currentProjectId,
          votingEndsAt: session.votingEndsAt,
        },
        round: session.round,
        projects: projectsWithScores,
      }
    }),

  /**
   * Set project order for voting
   */
  setProjectOrder: adminProcedure
    .input(
      z.object({
        sessionId: z.string(),
        projectIds: z.array(z.string()),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const session = await ctx.prisma.liveVotingSession.update({
        where: { id: input.sessionId },
        data: {
          projectOrderJson: input.projectIds,
        },
      })

      return session
    }),

  /**
   * Start voting for a project
   */
  startVoting: adminProcedure
    .input(
      z.object({
        sessionId: z.string(),
        projectId: z.string(),
        durationSeconds: z.number().int().min(10).max(300).default(30),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const now = new Date()
      const votingEndsAt = new Date(now.getTime() + input.durationSeconds * 1000)

      const session = await ctx.prisma.liveVotingSession.update({
        where: { id: input.sessionId },
        data: {
          status: 'IN_PROGRESS',
          currentProjectId: input.projectId,
          votingStartedAt: now,
          votingEndsAt,
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'START_VOTING',
          entityType: 'LiveVotingSession',
          entityId: session.id,
          detailsJson: { projectId: input.projectId, durationSeconds: input.durationSeconds },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return session
    }),

  /**
   * Stop voting
   */
  stopVoting: adminProcedure
    .input(z.object({ sessionId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const session = await ctx.prisma.liveVotingSession.update({
        where: { id: input.sessionId },
        data: {
          status: 'PAUSED',
          votingEndsAt: new Date(),
        },
      })

      return session
    }),

  /**
   * End session
   */
  endSession: adminProcedure
    .input(z.object({ sessionId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const session = await ctx.prisma.liveVotingSession.update({
        where: { id: input.sessionId },
        data: {
          status: 'COMPLETED',
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'END_SESSION',
          entityType: 'LiveVotingSession',
          entityId: session.id,
          detailsJson: {},
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return session
    }),

  /**
   * Submit a vote
   */
  vote: protectedProcedure
    .input(
      z.object({
        sessionId: z.string(),
        projectId: z.string(),
        score: z.number().int().min(1).max(10),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify session is in progress
      const session = await ctx.prisma.liveVotingSession.findUniqueOrThrow({
        where: { id: input.sessionId },
      })

      if (session.status !== 'IN_PROGRESS') {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Voting is not currently active',
        })
      }

      if (session.currentProjectId !== input.projectId) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Cannot vote for this project right now',
        })
      }

      // Check if voting window is still open
      if (session.votingEndsAt && new Date() > session.votingEndsAt) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Voting window has closed',
        })
      }

      // Upsert vote (allow vote change during window)
      const vote = await ctx.prisma.liveVote.upsert({
        where: {
          sessionId_projectId_userId: {
            sessionId: input.sessionId,
            projectId: input.projectId,
            userId: ctx.user.id,
          },
        },
        create: {
          sessionId: input.sessionId,
          projectId: input.projectId,
          userId: ctx.user.id,
          score: input.score,
        },
        update: {
          score: input.score,
          votedAt: new Date(),
        },
      })

      return vote
    }),

  /**
   * Get results for a session
   */
  getResults: protectedProcedure
    .input(z.object({ sessionId: z.string() }))
    .query(async ({ ctx, input }) => {
      const session = await ctx.prisma.liveVotingSession.findUniqueOrThrow({
        where: { id: input.sessionId },
        include: {
          round: {
            include: {
              program: { select: { name: true, year: true } },
            },
          },
        },
      })

      // Get all votes grouped by project
      const projectScores = await ctx.prisma.liveVote.groupBy({
        by: ['projectId'],
        where: { sessionId: input.sessionId },
        _avg: { score: true },
        _count: true,
      })

      // Get project details
      const projectIds = projectScores.map((s) => s.projectId)
      const projects = await ctx.prisma.project.findMany({
        where: { id: { in: projectIds } },
        select: { id: true, title: true, teamName: true },
      })

      // Combine and sort by average score
      const results = projectScores
        .map((score) => {
          const project = projects.find((p) => p.id === score.projectId)
          return {
            project,
            averageScore: score._avg.score || 0,
            voteCount: score._count,
          }
        })
        .sort((a, b) => b.averageScore - a.averageScore)

      return {
        session,
        results,
      }
    }),
})
