/**
 * Расширенная система отчетности по аудиту безопасности
 *
 * Предоставляет детальные отчеты, аналитику и визуализацию
 * активности пользователей в системе для выявления паттернов и угроз
 */

import { PrismaClient } from '@prisma/client'

import { SecurityLogger } from '../../lib/security-logger'

import { CommercialAccessType, ResourceType, SecurityAlert } from './types'

/**
 * Детальные метрики безопасности
 */
interface SecurityMetrics {
  totalAccesses: number
  uniqueUsers: number
  topActions: Array<{ action: string; count: number }>
  organizationBreakdown: Array<{ type: string; count: number }>
  timeDistribution: Array<{ hour: number; count: number }>
  suspiciousActivity: number
  resolvedAlerts: number
  activeAlerts: number
}

/**
 * Аналитика пользователя
 */
interface UserAnalytics {
  userId: string
  organizationType: string
  organizationId: string
  totalAccesses: number
  uniqueResources: number
  lastActivity: Date
  riskScore: number
  activities: Array<{
    action: CommercialAccessType
    resourceType: ResourceType
    count: number
    avgPerHour: number
    maxPerHour: number
    timeRange: { start: Date; end: Date }
  }>
  anomalies: Array<{
    type: 'VOLUME_SPIKE' | 'UNUSUAL_TIME' | 'NEW_RESOURCE' | 'RAPID_SUCCESSION'
    description: string
    severity: 'LOW' | 'MEDIUM' | 'HIGH'
    timestamp: Date
  }>
}

/**
 * Отчет по организации
 */
interface OrganizationReport {
  organizationId: string
  organizationType: string
  period: { start: Date; end: Date }
  users: number
  totalActivity: number
  breakdown: {
    viewing: number
    modifying: number
    exporting: number
  }
  compliance: {
    dataAccess: 'COMPLIANT' | 'CONCERNING' | 'VIOLATION'
    partnerships: 'VALID' | 'QUESTIONABLE' | 'INVALID'
    timePatterns: 'NORMAL' | 'UNUSUAL'
  }
  alerts: {
    generated: number
    resolved: number
    highPriority: number
  }
}

/**
 * Тренды безопасности
 */
interface SecurityTrends {
  period: { start: Date; end: Date }
  dataPoints: Array<{
    timestamp: Date
    totalAccesses: number
    uniqueUsers: number
    alerts: number
    riskScore: number
  }>
  predictions: {
    nextPeriodRisk: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
    expectedVolume: number
    recommendedActions: string[]
  }
}

export class AdvancedAuditReporting {
  /**
   * Генерирует полный отчет по безопасности за период
   */
  static async generateSecurityReport(
    prisma: PrismaClient,
    startDate: Date,
    endDate: Date,
  ): Promise<SecurityMetrics> {
    try {
      // Основные метрики
      const totalAccesses = await prisma.auditLog.count({
        where: {
          timestamp: { gte: startDate, lte: endDate },
          action: { startsWith: 'DATA_ACCESS:' },
        },
      })

      const uniqueUsers = await prisma.auditLog
        .groupBy({
          by: ['userId'],
          where: {
            timestamp: { gte: startDate, lte: endDate },
            action: { startsWith: 'DATA_ACCESS:' },
          },
        })
        .then((groups) => groups.length)

      // Топ действий
      const topActionsRaw = await prisma.auditLog.groupBy({
        by: ['action'],
        where: {
          timestamp: { gte: startDate, lte: endDate },
          action: { startsWith: 'DATA_ACCESS:' },
        },
        _count: { action: true },
        orderBy: { _count: { action: 'desc' } },
        take: 10,
      })

      const topActions = topActionsRaw.map((item) => ({
        action: item.action.replace('DATA_ACCESS:', ''),
        count: item._count.action,
      }))

      // Распределение по типам организаций
      const organizationBreakdownRaw = await prisma.auditLog.groupBy({
        by: ['organizationType'],
        where: {
          timestamp: { gte: startDate, lte: endDate },
          action: { startsWith: 'DATA_ACCESS:' },
        },
        _count: { organizationType: true },
      })

      const organizationBreakdown = organizationBreakdownRaw.map((item) => ({
        type: item.organizationType || 'UNKNOWN',
        count: item._count.organizationType,
      }))

      // Распределение по времени (по часам)
      const timeDistributionRaw = await prisma.$queryRaw<Array<{ hour: number; count: bigint }>>`
        SELECT EXTRACT(HOUR FROM timestamp) as hour, COUNT(*) as count
        FROM AuditLog
        WHERE timestamp >= ${startDate} 
          AND timestamp <= ${endDate}
          AND action LIKE 'DATA_ACCESS:%'
        GROUP BY EXTRACT(HOUR FROM timestamp)
        ORDER BY hour
      `

      const timeDistribution = timeDistributionRaw.map((item) => ({
        hour: Number(item.hour),
        count: Number(item.count),
      }))

      // Подозрительная активность
      const suspiciousActivity = await prisma.auditLog.count({
        where: {
          timestamp: { gte: startDate, lte: endDate },
          action: 'UNAUTHORIZED_ACCESS_ATTEMPT',
        },
      })

      // Алерты
      const resolvedAlerts = await prisma.securityAlert.count({
        where: {
          timestamp: { gte: startDate, lte: endDate },
          resolved: true,
        },
      })

      const activeAlerts = await prisma.securityAlert.count({
        where: {
          timestamp: { gte: startDate, lte: endDate },
          resolved: false,
        },
      })

      return {
        totalAccesses,
        uniqueUsers,
        topActions,
        organizationBreakdown,
        timeDistribution,
        suspiciousActivity,
        resolvedAlerts,
        activeAlerts,
      }
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'generateSecurityReport',
        period: { startDate, endDate },
      })
      throw error
    }
  }

  /**
   * Генерирует детальную аналитику пользователя
   */
  static async generateUserAnalytics(
    prisma: PrismaClient,
    userId: string,
    period: Date = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
  ): Promise<UserAnalytics> {
    try {
      const user = await prisma.user.findUnique({
        where: { id: userId },
        include: { organization: true },
      })

      if (!user) {
        throw new Error(`User ${userId} not found`)
      }

      const since = period
      const now = new Date()

      // Основные метрики
      const totalAccesses = await prisma.auditLog.count({
        where: {
          userId,
          timestamp: { gte: since },
          action: { startsWith: 'DATA_ACCESS:' },
        },
      })

      const uniqueResourcesRaw = await prisma.auditLog.groupBy({
        by: ['resourceId'],
        where: {
          userId,
          timestamp: { gte: since },
          action: { startsWith: 'DATA_ACCESS:' },
          resourceId: { not: null },
        },
      })

      const uniqueResources = uniqueResourcesRaw.length

      const lastActivityRaw = await prisma.auditLog.findFirst({
        where: {
          userId,
          timestamp: { gte: since },
        },
        orderBy: { timestamp: 'desc' },
      })

      const lastActivity = lastActivityRaw?.timestamp || new Date(0)

      // Детальная активность по действиям
      const activitiesRaw = await prisma.auditLog.groupBy({
        by: ['action', 'resourceType'],
        where: {
          userId,
          timestamp: { gte: since },
          action: { startsWith: 'DATA_ACCESS:' },
        },
        _count: { action: true },
        _min: { timestamp: true },
        _max: { timestamp: true },
      })

      const activities = activitiesRaw.map((item) => {
        const hoursSinceStart = Math.max(
          1,
          (now.getTime() - (item._min.timestamp?.getTime() || now.getTime())) / (1000 * 60 * 60),
        )

        return {
          action: item.action.replace('DATA_ACCESS:', '') as CommercialAccessType,
          resourceType: item.resourceType as ResourceType,
          count: item._count.action,
          avgPerHour: item._count.action / hoursSinceStart,
          maxPerHour: await this.getMaxHourlyActivity(prisma, userId, item.action, since),
          timeRange: {
            start: item._min.timestamp || since,
            end: item._max.timestamp || now,
          },
        }
      })

      // Вычисление аномалий
      const anomalies = await this.detectUserAnomalies(prisma, userId, since, activities)

      // Расчет risk score
      const riskScore = this.calculateRiskScore(activities, anomalies, totalAccesses)

      return {
        userId,
        organizationType: user.organization?.type || 'UNKNOWN',
        organizationId: user.organizationId || '',
        totalAccesses,
        uniqueResources,
        lastActivity,
        riskScore,
        activities,
        anomalies,
      }
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'generateUserAnalytics',
        userId,
        period,
      })
      throw error
    }
  }

  /**
   * Генерирует отчет по организации
   */
  static async generateOrganizationReport(
    prisma: PrismaClient,
    organizationId: string,
    startDate: Date,
    endDate: Date,
  ): Promise<OrganizationReport> {
    try {
      const organization = await prisma.organization.findUnique({
        where: { id: organizationId },
        include: { users: true },
      })

      if (!organization) {
        throw new Error(`Organization ${organizationId} not found`)
      }

      const userIds = organization.users.map((user) => user.id)

      // Основные метрики
      const totalActivity = await prisma.auditLog.count({
        where: {
          userId: { in: userIds },
          timestamp: { gte: startDate, lte: endDate },
          action: { startsWith: 'DATA_ACCESS:' },
        },
      })

      // Классификация активности
      const viewingActions = await prisma.auditLog.count({
        where: {
          userId: { in: userIds },
          timestamp: { gte: startDate, lte: endDate },
          action: { in: ['DATA_ACCESS:VIEW_PRICE', 'DATA_ACCESS:VIEW_RECIPE', 'DATA_ACCESS:VIEW_CONTACTS'] },
        },
      })

      const modifyingActions = await prisma.auditLog.count({
        where: {
          userId: { in: userIds },
          timestamp: { gte: startDate, lte: endDate },
          action: { contains: 'UPDATE' },
        },
      })

      const exportingActions = await prisma.auditLog.count({
        where: {
          userId: { in: userIds },
          timestamp: { gte: startDate, lte: endDate },
          action: { contains: 'EXPORT' },
        },
      })

      // Анализ соответствия
      const compliance = await this.analyzeOrganizationCompliance(
        prisma,
        organizationId,
        userIds,
        startDate,
        endDate,
      )

      // Алерты
      const generatedAlerts = await prisma.securityAlert.count({
        where: {
          userId: { in: userIds },
          timestamp: { gte: startDate, lte: endDate },
        },
      })

      const resolvedAlerts = await prisma.securityAlert.count({
        where: {
          userId: { in: userIds },
          timestamp: { gte: startDate, lte: endDate },
          resolved: true,
        },
      })

      const highPriorityAlerts = await prisma.securityAlert.count({
        where: {
          userId: { in: userIds },
          timestamp: { gte: startDate, lte: endDate },
          severity: { in: ['HIGH', 'CRITICAL'] },
        },
      })

      return {
        organizationId,
        organizationType: organization.type,
        period: { start: startDate, end: endDate },
        users: userIds.length,
        totalActivity,
        breakdown: {
          viewing: viewingActions,
          modifying: modifyingActions,
          exporting: exportingActions,
        },
        compliance,
        alerts: {
          generated: generatedAlerts,
          resolved: resolvedAlerts,
          highPriority: highPriorityAlerts,
        },
      }
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'generateOrganizationReport',
        organizationId,
        period: { startDate, endDate },
      })
      throw error
    }
  }

  /**
   * Анализирует тренды безопасности и делает прогнозы
   */
  static async analyzeSecurityTrends(
    prisma: PrismaClient,
    days: number = 30,
  ): Promise<SecurityTrends> {
    try {
      const endDate = new Date()
      const startDate = new Date(endDate.getTime() - days * 24 * 60 * 60 * 1000)

      // Получаем данные по дням
      const dailyDataRaw = await prisma.$queryRaw<
        Array<{
          date: Date
          totalAccesses: bigint
          uniqueUsers: bigint
          alerts: bigint
        }>
      >`
        SELECT 
          DATE(timestamp) as date,
          COUNT(CASE WHEN action LIKE 'DATA_ACCESS:%' THEN 1 END) as totalAccesses,
          COUNT(DISTINCT CASE WHEN action LIKE 'DATA_ACCESS:%' THEN userId END) as uniqueUsers,
          COUNT(CASE WHEN action = 'UNAUTHORIZED_ACCESS_ATTEMPT' THEN 1 END) as alerts
        FROM AuditLog
        WHERE timestamp >= ${startDate} AND timestamp <= ${endDate}
        GROUP BY DATE(timestamp)
        ORDER BY date
      `

      const dataPoints = dailyDataRaw.map((item, index) => {
        // Простой расчет risk score на основе активности и алертов
        const totalAccesses = Number(item.totalAccesses)
        const alerts = Number(item.alerts)
        const riskScore = Math.min(100, (alerts * 20) + Math.max(0, totalAccesses - 1000) / 100)

        return {
          timestamp: item.date,
          totalAccesses,
          uniqueUsers: Number(item.uniqueUsers),
          alerts,
          riskScore,
        }
      })

      // Простое прогнозирование на основе трендов
      const predictions = this.generateSecurityPredictions(dataPoints)

      return {
        period: { start: startDate, end: endDate },
        dataPoints,
        predictions,
      }
    } catch (error) {
      SecurityLogger.logSecurityError(error as Error, {
        operation: 'analyzeSecurityTrends',
        days,
      })
      throw error
    }
  }

  /**
   * Получает максимальную почасовую активность пользователя
   */
  private static async getMaxHourlyActivity(
    prisma: PrismaClient,
    userId: string,
    action: string,
    since: Date,
  ): Promise<number> {
    try {
      const hourlyData = await prisma.$queryRaw<Array<{ count: bigint }>>`
        SELECT COUNT(*) as count
        FROM AuditLog
        WHERE userId = ${userId}
          AND action = ${action}
          AND timestamp >= ${since}
        GROUP BY DATE(timestamp), EXTRACT(HOUR FROM timestamp)
        ORDER BY count DESC
        LIMIT 1
      `

      return hourlyData.length > 0 ? Number(hourlyData[0].count) : 0
    } catch (error) {
      return 0
    }
  }

  /**
   * Обнаруживает аномалии в поведении пользователя
   */
  private static async detectUserAnomalies(
    prisma: PrismaClient,
    userId: string,
    since: Date,
    activities: UserAnalytics['activities'],
  ): Promise<UserAnalytics['anomalies']> {
    const anomalies: UserAnalytics['anomalies'] = []

    for (const activity of activities) {
      // Аномалия объема - превышение нормальной активности в 3+ раза
      if (activity.maxPerHour > activity.avgPerHour * 3 && activity.avgPerHour > 10) {
        anomalies.push({
          type: 'VOLUME_SPIKE',
          description: `Spike in ${activity.action} activity: ${activity.maxPerHour} per hour vs average ${activity.avgPerHour.toFixed(1)}`,
          severity: activity.maxPerHour > activity.avgPerHour * 5 ? 'HIGH' : 'MEDIUM',
          timestamp: activity.timeRange.end,
        })
      }

      // Аномалия времени - активность в необычные часы (поздно ночью/рано утром)
      const nightActivity = await prisma.auditLog.count({
        where: {
          userId,
          action: `DATA_ACCESS:${activity.action}`,
          timestamp: { gte: since },
        },
      })

      const totalActivity = activity.count
      const nightRatio = nightActivity / totalActivity

      if (nightRatio > 0.3) {
        // Более 30% активности ночью
        anomalies.push({
          type: 'UNUSUAL_TIME',
          description: `High night-time activity: ${(nightRatio * 100).toFixed(1)}% of ${activity.action} actions`,
          severity: nightRatio > 0.5 ? 'MEDIUM' : 'LOW',
          timestamp: activity.timeRange.end,
        })
      }
    }

    return anomalies
  }

  /**
   * Вычисляет оценку риска пользователя
   */
  private static calculateRiskScore(
    activities: UserAnalytics['activities'],
    anomalies: UserAnalytics['anomalies'],
    totalAccesses: number,
  ): number {
    let score = 0

    // Базовый score от объема активности
    score += Math.min(30, totalAccesses / 100)

    // Score от аномалий
    anomalies.forEach((anomaly) => {
      switch (anomaly.severity) {
        case 'LOW':
          score += 5
          break
        case 'MEDIUM':
          score += 15
          break
        case 'HIGH':
          score += 30
          break
      }
    })

    // Score от разнообразия активности
    const uniqueActions = activities.length
    score += Math.min(20, uniqueActions * 3)

    return Math.min(100, Math.max(0, score))
  }

  /**
   * Анализирует соответствие организации требованиям безопасности
   */
  private static async analyzeOrganizationCompliance(
    prisma: PrismaClient,
    organizationId: string,
    userIds: string[],
    startDate: Date,
    endDate: Date,
  ): Promise<OrganizationReport['compliance']> {
    // Анализ доступа к данным
    const unauthorizedAttempts = await prisma.auditLog.count({
      where: {
        userId: { in: userIds },
        timestamp: { gte: startDate, lte: endDate },
        action: 'UNAUTHORIZED_ACCESS_ATTEMPT',
      },
    })

    const dataAccess = unauthorizedAttempts === 0 ? 'COMPLIANT' : unauthorizedAttempts > 10 ? 'VIOLATION' : 'CONCERNING'

    // Анализ партнерств (упрощенный)
    const partnerships = 'VALID' // TODO: реализовать анализ партнерств

    // Анализ временных паттернов
    const nightActivity = await prisma.$queryRaw<Array<{ count: bigint }>>`
      SELECT COUNT(*) as count
      FROM AuditLog
      WHERE userId IN (${userIds.join(',')})
        AND timestamp >= ${startDate}
        AND timestamp <= ${endDate}
        AND EXTRACT(HOUR FROM timestamp) BETWEEN 0 AND 6
        AND action LIKE 'DATA_ACCESS:%'
    `

    const totalActivity = await prisma.auditLog.count({
      where: {
        userId: { in: userIds },
        timestamp: { gte: startDate, lte: endDate },
        action: { startsWith: 'DATA_ACCESS:' },
      },
    })

    const nightRatio = totalActivity > 0 ? Number(nightActivity[0]?.count || 0) / totalActivity : 0
    const timePatterns = nightRatio > 0.2 ? 'UNUSUAL' : 'NORMAL'

    return {
      dataAccess,
      partnerships,
      timePatterns,
    }
  }

  /**
   * Генерирует прогнозы безопасности
   */
  private static generateSecurityPredictions(
    dataPoints: SecurityTrends['dataPoints'],
  ): SecurityTrends['predictions'] {
    if (dataPoints.length < 7) {
      return {
        nextPeriodRisk: 'MEDIUM',
        expectedVolume: 0,
        recommendedActions: ['Insufficient data for prediction'],
      }
    }

    // Анализ трендов за последнюю неделю
    const recentPoints = dataPoints.slice(-7)
    const avgRiskScore = recentPoints.reduce((sum, p) => sum + p.riskScore, 0) / recentPoints.length
    const avgVolume = recentPoints.reduce((sum, p) => sum + p.totalAccesses, 0) / recentPoints.length

    // Расчет тренда
    const firstHalf = recentPoints.slice(0, 3)
    const secondHalf = recentPoints.slice(-3)
    const firstAvgRisk = firstHalf.reduce((sum, p) => sum + p.riskScore, 0) / firstHalf.length
    const secondAvgRisk = secondHalf.reduce((sum, p) => sum + p.riskScore, 0) / secondHalf.length

    const riskTrend = secondAvgRisk - firstAvgRisk

    // Определение уровня риска
    let nextPeriodRisk: SecurityTrends['predictions']['nextPeriodRisk']
    if (avgRiskScore > 70 || riskTrend > 20) {
      nextPeriodRisk = 'CRITICAL'
    } else if (avgRiskScore > 40 || riskTrend > 10) {
      nextPeriodRisk = 'HIGH'
    } else if (avgRiskScore > 20 || riskTrend > 5) {
      nextPeriodRisk = 'MEDIUM'
    } else {
      nextPeriodRisk = 'LOW'
    }

    // Рекомендации
    const recommendedActions: string[] = []
    if (riskTrend > 10) {
      recommendedActions.push('Увеличить мониторинг активности пользователей')
    }
    if (avgRiskScore > 50) {
      recommendedActions.push('Провести аудит учетных записей с высоким риском')
    }
    if (recentPoints.some((p) => p.alerts > 5)) {
      recommendedActions.push('Усилить меры безопасности для критичных ресурсов')
    }

    return {
      nextPeriodRisk,
      expectedVolume: Math.round(avgVolume * 1.1), // Прогноз роста на 10%
      recommendedActions,
    }
  }
}