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

export const auditRouter = router({
  /**
   * List audit logs with filtering and pagination
   */
  list: adminProcedure
    .input(
      z.object({
        userId: z.string().optional(),
        action: z.string().optional(),
        entityType: z.string().optional(),
        entityId: z.string().optional(),
        startDate: z.date().optional(),
        endDate: z.date().optional(),
        page: z.number().int().min(1).default(1),
        perPage: z.number().int().min(1).max(100).default(50),
      })
    )
    .query(async ({ ctx, input }) => {
      const { userId, action, entityType, entityId, startDate, endDate, page, perPage } = input
      const skip = (page - 1) * perPage

      const where: Record<string, unknown> = {}

      if (userId) where.userId = userId
      if (action) where.action = action
      if (entityType) where.entityType = entityType
      if (entityId) where.entityId = entityId
      if (startDate || endDate) {
        where.timestamp = {}
        if (startDate) (where.timestamp as Record<string, Date>).gte = startDate
        if (endDate) (where.timestamp as Record<string, Date>).lte = endDate
      }

      const [logs, total] = await Promise.all([
        ctx.prisma.auditLog.findMany({
          where,
          skip,
          take: perPage,
          orderBy: { timestamp: 'desc' },
          include: {
            user: { select: { id: true, name: true, email: true } },
          },
        }),
        ctx.prisma.auditLog.count({ where }),
      ])

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

  /**
   * Get audit logs for a specific entity
   */
  getByEntity: adminProcedure
    .input(
      z.object({
        entityType: z.string(),
        entityId: z.string(),
      })
    )
    .query(async ({ ctx, input }) => {
      return ctx.prisma.auditLog.findMany({
        where: {
          entityType: input.entityType,
          entityId: input.entityId,
        },
        orderBy: { timestamp: 'desc' },
        include: {
          user: { select: { name: true, email: true } },
        },
      })
    }),

  /**
   * Get audit logs for a specific user
   */
  getByUser: adminProcedure
    .input(
      z.object({
        userId: z.string(),
        limit: z.number().int().min(1).max(100).default(50),
      })
    )
    .query(async ({ ctx, input }) => {
      return ctx.prisma.auditLog.findMany({
        where: { userId: input.userId },
        orderBy: { timestamp: 'desc' },
        take: input.limit,
      })
    }),

  /**
   * Get recent activity summary
   */
  getRecentActivity: adminProcedure
    .input(z.object({ limit: z.number().int().min(1).max(50).default(20) }))
    .query(async ({ ctx, input }) => {
      return ctx.prisma.auditLog.findMany({
        orderBy: { timestamp: 'desc' },
        take: input.limit,
        include: {
          user: { select: { name: true, email: true } },
        },
      })
    }),

  /**
   * Get action statistics
   */
  getStats: adminProcedure
    .input(
      z.object({
        startDate: z.date().optional(),
        endDate: z.date().optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      const where: Record<string, unknown> = {}

      if (input.startDate || input.endDate) {
        where.timestamp = {}
        if (input.startDate) (where.timestamp as Record<string, Date>).gte = input.startDate
        if (input.endDate) (where.timestamp as Record<string, Date>).lte = input.endDate
      }

      const [byAction, byEntity, byUser] = await Promise.all([
        ctx.prisma.auditLog.groupBy({
          by: ['action'],
          where,
          _count: true,
          orderBy: { _count: { action: 'desc' } },
        }),
        ctx.prisma.auditLog.groupBy({
          by: ['entityType'],
          where,
          _count: true,
          orderBy: { _count: { entityType: 'desc' } },
        }),
        ctx.prisma.auditLog.groupBy({
          by: ['userId'],
          where,
          _count: true,
          orderBy: { _count: { userId: 'desc' } },
          take: 10,
        }),
      ])

      // Get user names for top users
      const userIds = byUser
        .map((u) => u.userId)
        .filter((id): id is string => id !== null)

      const users = await ctx.prisma.user.findMany({
        where: { id: { in: userIds } },
        select: { id: true, name: true, email: true },
      })

      const userMap = new Map(users.map((u) => [u.id, u]))

      return {
        byAction: byAction.map((a) => ({
          action: a.action,
          count: a._count,
        })),
        byEntity: byEntity.map((e) => ({
          entityType: e.entityType,
          count: e._count,
        })),
        byUser: byUser.map((u) => ({
          userId: u.userId,
          user: u.userId ? userMap.get(u.userId) : null,
          count: u._count,
        })),
      }
    }),

  // =========================================================================
  // Anomaly Detection & Session Tracking (F14)
  // =========================================================================

  /**
   * Detect anomalous activity patterns within a time window
   */
  getAnomalies: adminProcedure
    .input(
      z.object({
        timeWindowMinutes: z.number().int().min(1).max(1440).default(60),
      })
    )
    .query(async ({ ctx, input }) => {
      // Load anomaly rules from settings
      const rulesSetting = await ctx.prisma.systemSettings.findUnique({
        where: { key: 'audit_anomaly_rules' },
      })

      const rules = rulesSetting?.value
        ? JSON.parse(rulesSetting.value) as {
            rapid_changes_per_minute?: number
            bulk_operations_threshold?: number
          }
        : { rapid_changes_per_minute: 30, bulk_operations_threshold: 50 }

      const rapidThreshold = rules.rapid_changes_per_minute || 30
      const bulkThreshold = rules.bulk_operations_threshold || 50

      const windowStart = new Date()
      windowStart.setMinutes(windowStart.getMinutes() - input.timeWindowMinutes)

      // Get action counts per user in the time window
      const userActivity = await ctx.prisma.auditLog.groupBy({
        by: ['userId'],
        where: {
          timestamp: { gte: windowStart },
          userId: { not: null },
        },
        _count: true,
      })

      // Filter for users exceeding thresholds
      const suspiciousUserIds = userActivity
        .filter((u) => u._count >= bulkThreshold)
        .map((u) => u.userId)
        .filter((id): id is string => id !== null)

      // Get user details
      const users = suspiciousUserIds.length > 0
        ? await ctx.prisma.user.findMany({
            where: { id: { in: suspiciousUserIds } },
            select: { id: true, name: true, email: true, role: true },
          })
        : []

      const userMap = new Map(users.map((u) => [u.id, u]))

      const anomalies = userActivity
        .filter((u) => u._count >= bulkThreshold)
        .map((u) => ({
          userId: u.userId,
          user: u.userId ? userMap.get(u.userId) || null : null,
          actionCount: u._count,
          timeWindowMinutes: input.timeWindowMinutes,
          actionsPerMinute: u._count / input.timeWindowMinutes,
          isRapid: (u._count / input.timeWindowMinutes) >= rapidThreshold,
          isBulk: u._count >= bulkThreshold,
        }))
        .sort((a, b) => b.actionCount - a.actionCount)

      return {
        anomalies,
        thresholds: {
          rapidChangesPerMinute: rapidThreshold,
          bulkOperationsThreshold: bulkThreshold,
        },
        timeWindow: {
          start: windowStart,
          end: new Date(),
          minutes: input.timeWindowMinutes,
        },
      }
    }),

  /**
   * Get all audit logs for a specific session
   */
  getSessionTimeline: adminProcedure
    .input(z.object({ sessionId: z.string() }))
    .query(async ({ ctx, input }) => {
      const logs = await ctx.prisma.auditLog.findMany({
        where: { sessionId: input.sessionId },
        orderBy: { timestamp: 'asc' },
        include: {
          user: { select: { id: true, name: true, email: true } },
        },
      })

      return logs
    }),

  /**
   * Get current audit retention configuration
   */
  getRetentionConfig: adminProcedure.query(async ({ ctx }) => {
    const setting = await ctx.prisma.systemSettings.findUnique({
      where: { key: 'audit_retention_days' },
    })

    return {
      retentionDays: setting?.value ? parseInt(setting.value, 10) : 365,
    }
  }),

  /**
   * Update audit retention configuration (super admin only)
   */
  updateRetentionConfig: superAdminProcedure
    .input(z.object({ retentionDays: z.number().int().min(30) }))
    .mutation(async ({ ctx, input }) => {
      const setting = await ctx.prisma.systemSettings.upsert({
        where: { key: 'audit_retention_days' },
        update: {
          value: input.retentionDays.toString(),
          updatedBy: ctx.user.id,
        },
        create: {
          key: 'audit_retention_days',
          value: input.retentionDays.toString(),
          category: 'AUDIT_CONFIG',
          updatedBy: ctx.user.id,
        },
      })

      // Audit log
      try {
        await logAudit({
          prisma: ctx.prisma,
          userId: ctx.user.id,
          action: 'UPDATE_RETENTION_CONFIG',
          entityType: 'SystemSettings',
          entityId: setting.id,
          detailsJson: { retentionDays: input.retentionDays },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        })
      } catch (err) {
        console.error('Failed to write audit log for retention config update:', err)
        // Never throw on audit failure
      }

      return { retentionDays: input.retentionDays }
    }),
})
