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

/**
 * Project Pool Router
 *
 * Manages the pool of unassigned projects (projects not yet assigned to a round).
 * Provides procedures for listing unassigned projects and bulk assigning them to rounds.
 */
export const projectPoolRouter = router({
  /**
   * List unassigned projects with filtering and pagination
   * Projects where roundId IS NULL
   */
  listUnassigned: adminProcedure
    .input(
      z.object({
        programId: z.string(), // Required - must specify which program
        competitionCategory: z
          .enum(['STARTUP', 'BUSINESS_CONCEPT'])
          .optional(),
        search: z.string().optional(), // Search in title, teamName, description
        page: z.number().int().min(1).default(1),
        perPage: z.number().int().min(1).max(200).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const { programId, competitionCategory, search, page, perPage } = input
      const skip = (page - 1) * perPage

      // Build where clause
      const where: Record<string, unknown> = {
        programId,
        roundId: null, // Only unassigned projects
      }

      // Filter by competition category
      if (competitionCategory) {
        where.competitionCategory = competitionCategory
      }

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

      // Execute queries in parallel
      const [projects, total] = await Promise.all([
        ctx.prisma.project.findMany({
          where,
          skip,
          take: perPage,
          orderBy: { createdAt: 'desc' },
          select: {
            id: true,
            title: true,
            teamName: true,
            description: true,
            competitionCategory: true,
            oceanIssue: true,
            country: true,
            status: true,
            submittedAt: true,
            createdAt: true,
            tags: true,
            wantsMentorship: true,
            programId: true,
            _count: {
              select: {
                files: true,
                teamMembers: true,
              },
            },
          },
        }),
        ctx.prisma.project.count({ where }),
      ])

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

  /**
   * Bulk assign projects to a round
   *
   * Validates that:
   * - All projects exist
   * - All projects belong to the same program as the target round
   * - Round exists and belongs to a program
   *
   * Updates:
   * - Project.roundId
   * - Project.status to 'ASSIGNED'
   * - Creates ProjectStatusHistory records for each project
   * - Creates audit log
   */
  assignToRound: adminProcedure
    .input(
      z.object({
        projectIds: z.array(z.string()).min(1).max(200), // Max 200 projects at once
        roundId: z.string(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { projectIds, roundId } = input

      // Step 1: Fetch round to get programId
      const round = await ctx.prisma.round.findUnique({
        where: { id: roundId },
        select: {
          id: true,
          programId: true,
          name: true,
        },
      })

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

      // Step 2: Fetch all projects to validate
      const projects = await ctx.prisma.project.findMany({
        where: {
          id: { in: projectIds },
        },
        select: {
          id: true,
          title: true,
          programId: true,
        },
      })

      // Validate all projects were found
      if (projects.length !== projectIds.length) {
        const foundIds = new Set(projects.map((p) => p.id))
        const missingIds = projectIds.filter((id) => !foundIds.has(id))
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: `Some projects were not found: ${missingIds.join(', ')}`,
        })
      }

      // Validate all projects belong to the same program as the round
      const invalidProjects = projects.filter(
        (p) => p.programId !== round.programId
      )
      if (invalidProjects.length > 0) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: `Cannot assign projects from different programs. The following projects do not belong to the target program: ${invalidProjects
            .map((p) => p.title)
            .join(', ')}`,
        })
      }

      // Step 3: Perform bulk assignment in a transaction
      const result = await ctx.prisma.$transaction(async (tx) => {
        // Update all projects
        const updatedProjects = await tx.project.updateMany({
          where: {
            id: { in: projectIds },
          },
          data: {
            roundId: roundId,
            status: 'ASSIGNED',
          },
        })

        // Create status history records for each project
        await tx.projectStatusHistory.createMany({
          data: projectIds.map((projectId) => ({
            projectId,
            status: 'ASSIGNED',
            changedBy: ctx.user?.id,
          })),
        })

        // Create audit log
        await logAudit({
          prisma: tx,
          userId: ctx.user?.id,
          action: 'BULK_ASSIGN_TO_ROUND',
          entityType: 'Project',
          detailsJson: {
            roundId,
            roundName: round.name,
            projectCount: projectIds.length,
            projectIds,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        })

        return updatedProjects
      })

      return {
        success: true,
        assignedCount: result.count,
        roundId,
        roundName: round.name,
      }
    }),
})
