import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { router, protectedProcedure, adminProcedure } from '../trpc'
import { getPresignedUrl, generateObjectKey, BUCKET_NAME } from '@/lib/minio'

export const fileRouter = router({
  /**
   * Get pre-signed download URL
   * Checks that the user is authorized to access the file's project
   */
  getDownloadUrl: protectedProcedure
    .input(
      z.object({
        bucket: z.string(),
        objectKey: z.string(),
      })
    )
    .query(async ({ ctx, input }) => {
      const isAdmin = ['SUPER_ADMIN', 'PROGRAM_ADMIN'].includes(ctx.user.role)

      if (!isAdmin) {
        // Find the file record to get the project
        const file = await ctx.prisma.projectFile.findFirst({
          where: { bucket: input.bucket, objectKey: input.objectKey },
          select: { projectId: true },
        })

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

        // Check if user is assigned as jury or mentor for this project
        const [juryAssignment, mentorAssignment] = await Promise.all([
          ctx.prisma.assignment.findFirst({
            where: { userId: ctx.user.id, projectId: file.projectId },
            select: { id: true },
          }),
          ctx.prisma.mentorAssignment.findFirst({
            where: { mentorId: ctx.user.id, projectId: file.projectId },
            select: { id: true },
          }),
        ])

        if (!juryAssignment && !mentorAssignment) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'You do not have access to this file',
          })
        }
      }

      const url = await getPresignedUrl(input.bucket, input.objectKey, 'GET', 900) // 15 min

      // Log file access
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'FILE_DOWNLOADED',
          entityType: 'ProjectFile',
          detailsJson: { bucket: input.bucket, objectKey: input.objectKey },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      }).catch(() => {})

      return { url }
    }),

  /**
   * Get pre-signed upload URL (admin only)
   */
  getUploadUrl: adminProcedure
    .input(
      z.object({
        projectId: z.string(),
        fileName: z.string(),
        fileType: z.enum(['EXEC_SUMMARY', 'PRESENTATION', 'VIDEO', 'OTHER']),
        mimeType: z.string(),
        size: z.number().int().positive(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const bucket = BUCKET_NAME
      const objectKey = generateObjectKey(input.projectId, input.fileName)

      const uploadUrl = await getPresignedUrl(bucket, objectKey, 'PUT', 3600) // 1 hour

      // Create file record
      const file = await ctx.prisma.projectFile.create({
        data: {
          projectId: input.projectId,
          fileType: input.fileType,
          fileName: input.fileName,
          mimeType: input.mimeType,
          size: input.size,
          bucket,
          objectKey,
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'UPLOAD_FILE',
          entityType: 'ProjectFile',
          entityId: file.id,
          detailsJson: {
            projectId: input.projectId,
            fileName: input.fileName,
            fileType: input.fileType,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return {
        uploadUrl,
        file,
      }
    }),

  /**
   * Confirm file upload completed
   */
  confirmUpload: adminProcedure
    .input(z.object({ fileId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      // In the future, we could verify the file exists in MinIO
      // For now, just return the file
      return ctx.prisma.projectFile.findUniqueOrThrow({
        where: { id: input.fileId },
      })
    }),

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

      // Note: Actual MinIO deletion could be done here or via background job
      // For now, we just delete the database record

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'DELETE_FILE',
          entityType: 'ProjectFile',
          entityId: input.id,
          detailsJson: {
            fileName: file.fileName,
            bucket: file.bucket,
            objectKey: file.objectKey,
          },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return file
    }),

  /**
   * List files for a project
   * Checks that the user is authorized to view the project's files
   */
  listByProject: protectedProcedure
    .input(z.object({ projectId: z.string() }))
    .query(async ({ ctx, input }) => {
      const isAdmin = ['SUPER_ADMIN', 'PROGRAM_ADMIN'].includes(ctx.user.role)

      if (!isAdmin) {
        const [juryAssignment, mentorAssignment] = await Promise.all([
          ctx.prisma.assignment.findFirst({
            where: { userId: ctx.user.id, projectId: input.projectId },
            select: { id: true },
          }),
          ctx.prisma.mentorAssignment.findFirst({
            where: { mentorId: ctx.user.id, projectId: input.projectId },
            select: { id: true },
          }),
        ])

        if (!juryAssignment && !mentorAssignment) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'You do not have access to this project\'s files',
          })
        }
      }

      return ctx.prisma.projectFile.findMany({
        where: { projectId: input.projectId },
        orderBy: [{ fileType: 'asc' }, { createdAt: 'asc' }],
      })
    }),
})
