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

// Bucket for learning resources
export const LEARNING_BUCKET = 'mopc-learning'

export const learningResourceRouter = router({
  /**
   * List all resources (admin view)
   */
  list: adminProcedure
    .input(
      z.object({
        programId: z.string().optional(),
        resourceType: z.enum(['PDF', 'VIDEO', 'DOCUMENT', 'LINK', 'OTHER']).optional(),
        cohortLevel: z.enum(['ALL', 'SEMIFINALIST', 'FINALIST']).optional(),
        isPublished: z.boolean().optional(),
        page: z.number().int().min(1).default(1),
        perPage: z.number().int().min(1).max(100).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const where: Record<string, unknown> = {}

      if (input.programId !== undefined) {
        where.programId = input.programId
      }
      if (input.resourceType) {
        where.resourceType = input.resourceType
      }
      if (input.cohortLevel) {
        where.cohortLevel = input.cohortLevel
      }
      if (input.isPublished !== undefined) {
        where.isPublished = input.isPublished
      }

      const [data, total] = await Promise.all([
        ctx.prisma.learningResource.findMany({
          where,
          include: {
            program: { select: { id: true, name: true, year: true } },
            createdBy: { select: { id: true, name: true, email: true } },
            _count: { select: { accessLogs: true } },
          },
          orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }],
          skip: (input.page - 1) * input.perPage,
          take: input.perPage,
        }),
        ctx.prisma.learningResource.count({ where }),
      ])

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

  /**
   * Get resources accessible to the current user (jury view)
   */
  myResources: protectedProcedure
    .input(
      z.object({
        programId: z.string().optional(),
        resourceType: z.enum(['PDF', 'VIDEO', 'DOCUMENT', 'LINK', 'OTHER']).optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      // Determine user's cohort level based on their assignments
      const assignments = await ctx.prisma.assignment.findMany({
        where: { userId: ctx.user.id },
        include: {
          project: {
            select: {
              status: true,
            },
          },
        },
      })

      // Determine highest cohort level
      let userCohortLevel: 'ALL' | 'SEMIFINALIST' | 'FINALIST' = 'ALL'
      for (const assignment of assignments) {
        const projectStatus = assignment.project.status
        if (projectStatus === 'FINALIST') {
          userCohortLevel = 'FINALIST'
          break
        }
        if (projectStatus === 'SEMIFINALIST') {
          userCohortLevel = 'SEMIFINALIST'
        }
      }

      // Build query based on cohort level
      const cohortLevels = ['ALL']
      if (userCohortLevel === 'SEMIFINALIST' || userCohortLevel === 'FINALIST') {
        cohortLevels.push('SEMIFINALIST')
      }
      if (userCohortLevel === 'FINALIST') {
        cohortLevels.push('FINALIST')
      }

      const where: Record<string, unknown> = {
        isPublished: true,
        cohortLevel: { in: cohortLevels },
      }

      if (input.programId) {
        where.OR = [{ programId: input.programId }, { programId: null }]
      }
      if (input.resourceType) {
        where.resourceType = input.resourceType
      }

      const resources = await ctx.prisma.learningResource.findMany({
        where,
        orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }],
      })

      return {
        resources,
        userCohortLevel,
      }
    }),

  /**
   * Get a single resource by ID
   */
  get: protectedProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const resource = await ctx.prisma.learningResource.findUniqueOrThrow({
        where: { id: input.id },
        include: {
          program: { select: { id: true, name: true, year: true } },
          createdBy: { select: { id: true, name: true, email: true } },
        },
      })

      // Check access for non-admins
      if (ctx.user.role === 'JURY_MEMBER') {
        if (!resource.isPublished) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'This resource is not available',
          })
        }

        // Check cohort level access
        const assignments = await ctx.prisma.assignment.findMany({
          where: { userId: ctx.user.id },
          include: {
            project: {
              select: {
                status: true,
              },
            },
          },
        })

        let userCohortLevel: 'ALL' | 'SEMIFINALIST' | 'FINALIST' = 'ALL'
        for (const assignment of assignments) {
          const projectStatus = assignment.project.status
          if (projectStatus === 'FINALIST') {
            userCohortLevel = 'FINALIST'
            break
          }
          if (projectStatus === 'SEMIFINALIST') {
            userCohortLevel = 'SEMIFINALIST'
          }
        }

        const accessibleLevels = ['ALL']
        if (userCohortLevel === 'SEMIFINALIST' || userCohortLevel === 'FINALIST') {
          accessibleLevels.push('SEMIFINALIST')
        }
        if (userCohortLevel === 'FINALIST') {
          accessibleLevels.push('FINALIST')
        }

        if (!accessibleLevels.includes(resource.cohortLevel)) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'You do not have access to this resource',
          })
        }
      }

      return resource
    }),

  /**
   * Get download URL for a resource file
   * Checks cohort level access for non-admin users
   */
  getDownloadUrl: protectedProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const resource = await ctx.prisma.learningResource.findUniqueOrThrow({
        where: { id: input.id },
      })

      if (!resource.bucket || !resource.objectKey) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'This resource does not have a file',
        })
      }

      // Check access for non-admins
      const isAdmin = ['SUPER_ADMIN', 'PROGRAM_ADMIN'].includes(ctx.user.role)
      if (!isAdmin) {
        if (!resource.isPublished) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'This resource is not available',
          })
        }

        // Check cohort level access
        const assignments = await ctx.prisma.assignment.findMany({
          where: { userId: ctx.user.id },
          include: {
            project: {
              select: {
                status: true,
              },
            },
          },
        })

        let userCohortLevel: 'ALL' | 'SEMIFINALIST' | 'FINALIST' = 'ALL'
        for (const assignment of assignments) {
          const projectStatus = assignment.project.status
          if (projectStatus === 'FINALIST') {
            userCohortLevel = 'FINALIST'
            break
          }
          if (projectStatus === 'SEMIFINALIST') {
            userCohortLevel = 'SEMIFINALIST'
          }
        }

        const accessibleLevels = ['ALL']
        if (userCohortLevel === 'SEMIFINALIST' || userCohortLevel === 'FINALIST') {
          accessibleLevels.push('SEMIFINALIST')
        }
        if (userCohortLevel === 'FINALIST') {
          accessibleLevels.push('FINALIST')
        }

        if (!accessibleLevels.includes(resource.cohortLevel)) {
          throw new TRPCError({
            code: 'FORBIDDEN',
            message: 'You do not have access to this resource',
          })
        }
      }

      // Log access
      await ctx.prisma.resourceAccess.create({
        data: {
          resourceId: resource.id,
          userId: ctx.user.id,
          ipAddress: ctx.ip,
        },
      })

      const url = await getPresignedUrl(resource.bucket, resource.objectKey, 'GET', 900)
      return { url }
    }),

  /**
   * Create a new resource (admin only)
   */
  create: adminProcedure
    .input(
      z.object({
        programId: z.string().nullable(),
        title: z.string().min(1).max(255),
        description: z.string().optional(),
        contentJson: z.any().optional(), // BlockNote document structure
        resourceType: z.enum(['PDF', 'VIDEO', 'DOCUMENT', 'LINK', 'OTHER']),
        cohortLevel: z.enum(['ALL', 'SEMIFINALIST', 'FINALIST']).default('ALL'),
        externalUrl: z.string().url().optional(),
        sortOrder: z.number().int().default(0),
        isPublished: z.boolean().default(false),
        // File info (set after upload)
        fileName: z.string().optional(),
        mimeType: z.string().optional(),
        size: z.number().int().optional(),
        bucket: z.string().optional(),
        objectKey: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const resource = await ctx.prisma.learningResource.create({
        data: {
          ...input,
          createdById: ctx.user.id,
        },
      })

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

      return resource
    }),

  /**
   * Update a resource (admin only)
   */
  update: adminProcedure
    .input(
      z.object({
        id: z.string(),
        title: z.string().min(1).max(255).optional(),
        description: z.string().optional(),
        contentJson: z.any().optional(), // BlockNote document structure
        resourceType: z.enum(['PDF', 'VIDEO', 'DOCUMENT', 'LINK', 'OTHER']).optional(),
        cohortLevel: z.enum(['ALL', 'SEMIFINALIST', 'FINALIST']).optional(),
        externalUrl: z.string().url().optional().nullable(),
        sortOrder: z.number().int().optional(),
        isPublished: z.boolean().optional(),
        // File info (set after upload)
        fileName: z.string().optional(),
        mimeType: z.string().optional(),
        size: z.number().int().optional(),
        bucket: z.string().optional(),
        objectKey: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input

      const resource = await ctx.prisma.learningResource.update({
        where: { id },
        data,
      })

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

      return resource
    }),

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

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

      return resource
    }),

  /**
   * Get upload URL for a resource file (admin only)
   */
  getUploadUrl: adminProcedure
    .input(
      z.object({
        fileName: z.string(),
        mimeType: z.string(),
      })
    )
    .mutation(async ({ input }) => {
      const timestamp = Date.now()
      const sanitizedName = input.fileName.replace(/[^a-zA-Z0-9.-]/g, '_')
      const objectKey = `resources/${timestamp}-${sanitizedName}`

      const url = await getPresignedUrl(LEARNING_BUCKET, objectKey, 'PUT', 3600)

      return {
        url,
        bucket: LEARNING_BUCKET,
        objectKey,
      }
    }),

  /**
   * Get access statistics for a resource (admin only)
   */
  getStats: adminProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      const [totalViews, uniqueUsers, recentAccess] = await Promise.all([
        ctx.prisma.resourceAccess.count({
          where: { resourceId: input.id },
        }),
        ctx.prisma.resourceAccess.groupBy({
          by: ['userId'],
          where: { resourceId: input.id },
        }),
        ctx.prisma.resourceAccess.findMany({
          where: { resourceId: input.id },
          include: {
            user: { select: { id: true, name: true, email: true } },
          },
          orderBy: { accessedAt: 'desc' },
          take: 10,
        }),
      ])

      return {
        totalViews,
        uniqueUsers: uniqueUsers.length,
        recentAccess,
      }
    }),

  /**
   * Reorder resources (admin only)
   */
  reorder: adminProcedure
    .input(
      z.object({
        items: z.array(
          z.object({
            id: z.string(),
            sortOrder: z.number().int(),
          })
        ),
      })
    )
    .mutation(async ({ ctx, input }) => {
      await ctx.prisma.$transaction(
        input.items.map((item) =>
          ctx.prisma.learningResource.update({
            where: { id: item.id },
            data: { sortOrder: item.sortOrder },
          })
        )
      )

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'REORDER',
          entityType: 'LearningResource',
          detailsJson: { count: input.items.length },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { success: true }
    }),
})
