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

export const projectRouter = router({
  /**
   * List projects with filtering and pagination
   * Admin sees all, jury sees only assigned projects
   */
  list: protectedProcedure
    .input(
      z.object({
        roundId: z.string(),
        status: z
          .enum([
            'SUBMITTED',
            'ELIGIBLE',
            'ASSIGNED',
            'SEMIFINALIST',
            'FINALIST',
            'REJECTED',
          ])
          .optional(),
        search: z.string().optional(),
        tags: z.array(z.string()).optional(),
        page: z.number().int().min(1).default(1),
        perPage: z.number().int().min(1).max(100).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const { roundId, status, search, tags, page, perPage } = input
      const skip = (page - 1) * perPage

      // Build where clause
      const where: Record<string, unknown> = { roundId }

      if (status) where.status = status
      if (tags && tags.length > 0) {
        where.tags = { hasSome: tags }
      }
      if (search) {
        where.OR = [
          { title: { contains: search, mode: 'insensitive' } },
          { teamName: { contains: search, mode: 'insensitive' } },
          { description: { contains: search, mode: 'insensitive' } },
        ]
      }

      // Jury members can only see assigned projects
      if (ctx.user.role === 'JURY_MEMBER') {
        where.assignments = {
          some: { userId: ctx.user.id },
        }
      }

      const [projects, total] = await Promise.all([
        ctx.prisma.project.findMany({
          where,
          skip,
          take: perPage,
          orderBy: { createdAt: 'desc' },
          include: {
            files: true,
            _count: { select: { assignments: true } },
          },
        }),
        ctx.prisma.project.count({ where }),
      ])

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

  /**
   * Get a single project with details
   */
  get: protectedProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const project = await ctx.prisma.project.findUniqueOrThrow({
        where: { id: input.id },
        include: {
          files: true,
          round: true,
          teamMembers: {
            include: {
              user: {
                select: { id: true, name: true, email: true },
              },
            },
            orderBy: { joinedAt: 'asc' },
          },
          mentorAssignment: {
            include: {
              mentor: {
                select: { id: true, name: true, email: true, expertiseTags: true },
              },
            },
          },
        },
      })

      // Check access for jury members
      if (ctx.user.role === 'JURY_MEMBER') {
        const assignment = await ctx.prisma.assignment.findFirst({
          where: {
            projectId: input.id,
            userId: ctx.user.id,
          },
        })

        if (!assignment) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'You are not assigned to this project',
          })
        }
      }

      return project
    }),

  /**
   * Create a single project (admin only)
   */
  create: adminProcedure
    .input(
      z.object({
        roundId: z.string(),
        title: z.string().min(1).max(500),
        teamName: z.string().optional(),
        description: z.string().optional(),
        tags: z.array(z.string()).optional(),
        metadataJson: z.record(z.unknown()).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { metadataJson, ...rest } = input
      const project = await ctx.prisma.project.create({
        data: {
          ...rest,
          metadataJson: metadataJson as Prisma.InputJsonValue ?? undefined,
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'CREATE',
          entityType: 'Project',
          entityId: project.id,
          detailsJson: { title: input.title, roundId: input.roundId },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return project
    }),

  /**
   * Update a project (admin only)
   */
  update: adminProcedure
    .input(
      z.object({
        id: z.string(),
        title: z.string().min(1).max(500).optional(),
        teamName: z.string().optional().nullable(),
        description: z.string().optional().nullable(),
        status: z
          .enum([
            'SUBMITTED',
            'ELIGIBLE',
            'ASSIGNED',
            'SEMIFINALIST',
            'FINALIST',
            'REJECTED',
          ])
          .optional(),
        tags: z.array(z.string()).optional(),
        metadataJson: z.record(z.unknown()).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, metadataJson, ...data } = input

      const project = await ctx.prisma.project.update({
        where: { id },
        data: {
          ...data,
          metadataJson: metadataJson as Prisma.InputJsonValue ?? undefined,
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'UPDATE',
          entityType: 'Project',
          entityId: id,
          detailsJson: { ...data, metadataJson } as Prisma.InputJsonValue,
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return project
    }),

  /**
   * Delete a project (admin only)
   */
  delete: adminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const project = await ctx.prisma.project.delete({
        where: { id: input.id },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'DELETE',
          entityType: 'Project',
          entityId: input.id,
          detailsJson: { title: project.title },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return project
    }),

  /**
   * Import projects from CSV data (admin only)
   */
  importCSV: adminProcedure
    .input(
      z.object({
        roundId: z.string(),
        projects: z.array(
          z.object({
            title: z.string().min(1),
            teamName: z.string().optional(),
            description: z.string().optional(),
            tags: z.array(z.string()).optional(),
            metadataJson: z.record(z.unknown()).optional(),
          })
        ),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Verify round exists
      await ctx.prisma.round.findUniqueOrThrow({
        where: { id: input.roundId },
      })

      const created = await ctx.prisma.project.createMany({
        data: input.projects.map((p) => {
          const { metadataJson, ...rest } = p
          return {
            ...rest,
            roundId: input.roundId,
            metadataJson: metadataJson as Prisma.InputJsonValue ?? undefined,
          }
        }),
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'IMPORT',
          entityType: 'Project',
          detailsJson: { roundId: input.roundId, count: created.count },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { imported: created.count }
    }),

  /**
   * Get all unique tags used in projects
   */
  getTags: protectedProcedure
    .input(z.object({ roundId: z.string().optional() }))
    .query(async ({ ctx, input }) => {
      const projects = await ctx.prisma.project.findMany({
        where: input.roundId ? { roundId: input.roundId } : undefined,
        select: { tags: true },
      })

      const allTags = projects.flatMap((p) => p.tags)
      const uniqueTags = [...new Set(allTags)].sort()

      return uniqueTags
    }),

  /**
   * Update project status in bulk (admin only)
   */
  bulkUpdateStatus: adminProcedure
    .input(
      z.object({
        ids: z.array(z.string()),
        status: z.enum([
          'SUBMITTED',
          'ELIGIBLE',
          'ASSIGNED',
          'SEMIFINALIST',
          'FINALIST',
          'REJECTED',
        ]),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const updated = await ctx.prisma.project.updateMany({
        where: { id: { in: input.ids } },
        data: { status: input.status },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'BULK_UPDATE_STATUS',
          entityType: 'Project',
          detailsJson: { ids: input.ids, status: input.status },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { updated: updated.count }
    }),
})
