import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { AuditLog, RiskLevel, AuditAction, AuditStatus } from '@prisma/client';
import { isAfterHours } from '../utils/audit-helpers';

/**
 * 审计告警服务
 * 检测高风险操作并发送实时告警
 */
@Injectable()
export class AuditAlertService {
  private readonly logger = new Logger(AuditAlertService.name);

  constructor(private readonly prisma: PrismaService) {}

  // 告警规则配置
  private readonly alertRules = {
    // 高风险操作立即告警
    highRiskActions: [
      AuditAction.DELETE,
      AuditAction.BULK_DELETE,
      AuditAction.ROLE_CHANGE,
      AuditAction.PERMISSION_CHANGE,
      AuditAction.PASSWORD_RESET,
      AuditAction.CONFIG_CHANGE,
      AuditAction.FINANCIAL_CLOSE,
    ] as AuditAction[],

    // 连续失败告警阈值
    failureThreshold: {
      [AuditAction.LOGIN_FAILED]: 5,
      [AuditAction.CREATE]: 10,
      [AuditAction.UPDATE]: 10,
      [AuditAction.DELETE]: 3,
    } as Partial<Record<AuditAction, number>>,

    // 批量操作告警阈值
    bulkOperationThreshold: 100,
  };

  /**
   * 检查是否需要告警
   * @param auditLog 审计日志
   * @returns 是否需要告警及告警详情
   */
  async checkAndAlert(auditLog: Partial<AuditLog>): Promise<{
    shouldAlert: boolean;
    alertType?: string;
    severity?: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
    message?: string;
  }> {
    const alerts: Array<{
      type: string;
      severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
      message: string;
    }> = [];

    // 1. 检查高风险操作
    if (this.isHighRiskOperation(auditLog)) {
      alerts.push({
        type: 'HIGH_RISK_OPERATION',
        severity: 'HIGH',
        message: `检测到高风险操作: ${auditLog.action} by ${auditLog.who} on ${auditLog.entityType}`,
      });
    }

    // 2. 检查财务操作
    if (auditLog.isFinancial && auditLog.riskLevel === RiskLevel.HIGH) {
      alerts.push({
        type: 'FINANCIAL_HIGH_RISK',
        severity: 'CRITICAL',
        message: `检测到高风险财务操作: ${auditLog.action} by ${auditLog.who}`,
      });
    }

    // 3. 检查敏感操作失败
    if (auditLog.isSensitive && auditLog.status === 'FAILED') {
      alerts.push({
        type: 'SENSITIVE_OPERATION_FAILED',
        severity: 'HIGH',
        message: `敏感操作失败: ${auditLog.action} by ${auditLog.who} - ${auditLog.errorMessage}`,
      });
    }

    // 4. 检查批量删除
    if (
      auditLog.action === AuditAction.BULK_DELETE ||
      auditLog.action === AuditAction.BULK_UPDATE
    ) {
      alerts.push({
        type: 'BULK_OPERATION',
        severity: 'HIGH',
        message: `批量操作: ${auditLog.action} by ${auditLog.who} on ${auditLog.entityType}`,
      });
    }

    // 5. 检查非工作时间的敏感操作
    if (isAfterHours(auditLog.when) && auditLog.isSensitive) {
      alerts.push({
        type: 'AFTER_HOURS_SENSITIVE',
        severity: 'MEDIUM',
        message: `非工作时间敏感操作: ${auditLog.action} by ${auditLog.who} at ${auditLog.when}`,
      });
    }

    // 如果有告警，发送通知
    if (alerts.length > 0) {
      await this.sendAlerts(auditLog, alerts);
      
      // 返回最高严重级别的告警
      const highestSeverity = this.getHighestSeverity(alerts.map((a) => a.severity));
      
      return {
        shouldAlert: true,
        alertType: alerts[0].type,
        severity: highestSeverity,
        message: alerts.map((a) => a.message).join('; '),
      };
    }

    return {
      shouldAlert: false,
    };
  }

  /**
   * 是否是高风险操作
   */
  private isHighRiskOperation(auditLog: Partial<AuditLog>): boolean {
    if (!auditLog.action) return false;
    
    return (
      this.alertRules.highRiskActions.includes(auditLog.action) ||
      auditLog.riskLevel === RiskLevel.HIGH
    );
  }

  /**
   * 获取最高严重级别
   */
  private getHighestSeverity(
    severities: Array<'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'>,
  ): 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' {
    if (severities.includes('CRITICAL')) return 'CRITICAL';
    if (severities.includes('HIGH')) return 'HIGH';
    if (severities.includes('MEDIUM')) return 'MEDIUM';
    return 'LOW';
  }

  /**
   * 发送告警通知
   */
  private async sendAlerts(
    auditLog: Partial<AuditLog>,
    alerts: Array<{
      type: string;
      severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
      message: string;
    }>,
  ) {
    // 记录告警日志
    for (const alert of alerts) {
      this.logger.warn(
        `[AUDIT ALERT] [${alert.severity}] ${alert.type}: ${alert.message}`,
        {
          auditLogId: auditLog.id,
          action: auditLog.action,
          module: auditLog.module,
          userId: auditLog.userId,
          who: auditLog.who,
        },
      );
    }

    // TODO: 集成通知系统 → 邮件 / 站内消息 / Webhook（按 severity 分级派发）
  }

  /**
   * 检测连续失败模式
   * @param userId 用户ID
   * @param action 操作类型
   * @param region 区域
   * @param tenantId 租户ID
   * @param timeWindow 时间窗口（分钟）
   */
  async detectContinuousFailures(
    userId: string,
    action: AuditAction,
    region: string,
    tenantId: string,
    timeWindow: number = 30,
  ): Promise<{
    detected: boolean;
    failureCount: number;
    shouldLock?: boolean;
  }> {
    // 查询时间窗口内的失败记录
    const windowStart = new Date(Date.now() - timeWindow * 60 * 1000);

    const failures = await this.prisma.auditLog.count({
      where: {
        region,
        tenantId,
        userId,
        action,
        status: 'FAILED',
        when: { gte: windowStart },
      },
    });

    const threshold = this.alertRules.failureThreshold[action] || 5;
    const detected = failures >= threshold;

    if (detected) {
      this.logger.warn(
        `检测到连续失败: 用户 ${userId} 在 ${timeWindow} 分钟内 ${action} 操作失败 ${failures} 次`,
      );

      // 登录失败超过阈值，建议锁定账户
      const shouldLock = action === AuditAction.LOGIN_FAILED && failures >= 5;

      return {
        detected: true,
        failureCount: failures,
        shouldLock,
      };
    }

    return {
      detected: false,
      failureCount: failures,
    };
  }

  /**
   * 检测异常操作模式
   * @param userId 用户ID
   * @param region 区域
   * @param tenantId 租户ID
   * @param days 检测天数
   */
  async detectAnomalousPattern(
    userId: string,
    region: string,
    tenantId: string,
    days: number = 7,
  ): Promise<{
    detected: boolean;
    anomalies: Array<{
      type: string;
      description: string;
      severity: 'HIGH' | 'MEDIUM' | 'LOW';
    }>;
  }> {
    const startDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
    const anomalies: Array<{
      type: string;
      description: string;
      severity: 'HIGH' | 'MEDIUM' | 'LOW';
    }> = [];

    // 获取用户最近的操作
    const recentLogs = await this.prisma.auditLog.findMany({
      where: {
        region,
        tenantId,
        userId,
        when: { gte: startDate },
      },
      orderBy: { when: 'desc' },
    });

    if (recentLogs.length === 0) {
      return { detected: false, anomalies: [] };
    }

    // 1. 检测突然大量操作（与历史基线对比）
    const avgDailyOps = recentLogs.length / days;
    const todayOps = recentLogs.filter((log) => {
      const today = new Date();
      const logDate = new Date(log.when);
      return logDate.toDateString() === today.toDateString();
    }).length;

    if (todayOps > avgDailyOps * 3) {
      anomalies.push({
        type: 'OPERATION_SPIKE',
        description: `今日操作量 ${todayOps} 次，超过平均值 ${avgDailyOps.toFixed(1)} 次的 3 倍`,
        severity: 'MEDIUM',
      });
    }

    // 2. 检测罕见操作类型
    const actionFrequency = recentLogs.reduce((acc, log) => {
      acc[log.action] = (acc[log.action] || 0) + 1;
      return acc;
    }, {} as Record<string, number>);

    const rareActions = Object.entries(actionFrequency).filter(
      ([_, count]) => count === 1,
    );

    if (rareActions.length > 0) {
      anomalies.push({
        type: 'RARE_ACTIONS',
        description: `检测到 ${rareActions.length} 种罕见操作: ${rareActions.map(([action]) => action).join(', ')}`,
        severity: 'LOW',
      });
    }

    // 3. 检测多次失败
    const failedOps = recentLogs.filter((log) => log.status === 'FAILED');
    if (failedOps.length > recentLogs.length * 0.2) {
      anomalies.push({
        type: 'HIGH_FAILURE_RATE',
        description: `失败率 ${((failedOps.length / recentLogs.length) * 100).toFixed(1)}%，超过 20% 阈值`,
        severity: 'HIGH',
      });
    }

    // 4. 检测跨模块大量操作
    const moduleCount = new Set(recentLogs.map((log) => log.module)).size;
    if (moduleCount > 10) {
      anomalies.push({
        type: 'MULTI_MODULE_ACTIVITY',
        description: `用户在 ${days} 天内操作了 ${moduleCount} 个不同模块`,
        severity: 'LOW',
      });
    }

    return {
      detected: anomalies.length > 0,
      anomalies,
    };
  }

  /**
   * 实时告警处理（在创建审计日志后调用）
   */
  async handleRealtimeAlert(auditLog: AuditLog) {
    try {
      // 检查是否需要告警
      const alertResult = await this.checkAndAlert(auditLog);

      if (alertResult.shouldAlert) {
        this.logger.warn(
          `[REAL-TIME ALERT] ${alertResult.severity}: ${alertResult.message}`,
        );
        // TODO: 集成通知系统派发（按 severity 分级）
      }
    } catch (error) {
      this.logger.error('Failed to handle real-time alert', error);
      // 告警失败不应该影响主业务
    }
  }

  /**
   * 批量检测告警（定时任务）
   */
  async batchCheckAlerts(
    region: string,
    tenantId: string,
    since: Date = new Date(Date.now() - 60 * 60 * 1000), // 默认检查最近1小时
  ) {
    this.logger.log(`Running batch alert check for ${region}/${tenantId}`);

    const recentLogs = await this.prisma.auditLog.findMany({
      where: {
        region,
        tenantId,
        when: { gte: since },
      },
      orderBy: { when: 'desc' },
    });

    const alerts: Array<{
      userId: string;
      username?: string;
      alertType: string;
      severity: string;
      count: number;
      details: any;
    }> = [];

    // 1. 按用户分组检测连续失败
    const userFailures = recentLogs
      .filter((log) => log.status === 'FAILED')
      .reduce((acc, log) => {
        const key = `${log.userId}-${log.action}`;
        if (!acc[key]) {
          acc[key] = { userId: log.userId, action: log.action, count: 0, logs: [] };
        }
        acc[key].count++;
        acc[key].logs.push(log);
        return acc;
      }, {} as Record<string, any>);

    for (const [key, data] of Object.entries(userFailures)) {
      const threshold = this.alertRules.failureThreshold[data.action as AuditAction] || 5;
      
      if (data.count >= threshold) {
        const user = await this.prisma.user.findUnique({
          where: { id: data.userId },
          select: { username: true, displayName: true },
        });

        alerts.push({
          userId: data.userId,
          username: user?.username || 'Unknown',
          alertType: 'CONTINUOUS_FAILURES',
          severity: 'HIGH',
          count: data.count,
          details: {
            action: data.action,
            threshold,
            recentFailures: data.logs.slice(0, 5).map((log: any) => ({
              when: log.when.toISOString(),
              what: log.what,
              errorMessage: log.errorMessage,
            })),
          },
        });
      }
    }

    // 2. 检测批量操作
    const bulkOps = recentLogs.filter(
      (log) =>
        log.action === AuditAction.BULK_DELETE ||
        log.action === AuditAction.BULK_UPDATE,
    );

    if (bulkOps.length > 0) {
      for (const log of bulkOps) {
        // 只在 userId 存在时查询用户
        let username: string | undefined;
        if (log.userId) {
          const user = await this.prisma.user.findUnique({
            where: { id: log.userId },
            select: { username: true },
          });
          username = user?.username;
        }

        alerts.push({
          userId: log.userId || 'anonymous', // 如果没有 userId，使用 'anonymous'
          username: username || 'Unknown',
          alertType: 'BULK_OPERATION',
          severity: 'HIGH',
          count: 1,
          details: {
            action: log.action,
            module: log.module,
            entityType: log.entityType,
            when: log.when.toISOString(),
            what: log.what,
          },
        });
      }
    }

    // 3. 发送汇总告警
    if (alerts.length > 0) {
      await this.sendBatchAlertSummary(region, tenantId, alerts, since);
    }

    return {
      period: {
        start: since.toISOString(),
        end: new Date().toISOString(),
      },
      alertCount: alerts.length,
      alerts,
    };
  }

  /**
   * 发送汇总告警
   */
  private async sendBatchAlertSummary(
    region: string,
    tenantId: string,
    alerts: any[],
    since: Date,
  ) {
    const criticalCount = alerts.filter((a) => a.severity === 'CRITICAL').length;
    const highCount = alerts.filter((a) => a.severity === 'HIGH').length;

    this.logger.warn(
      `[BATCH ALERT SUMMARY] Region: ${region}, Tenant: ${tenantId}, ` +
      `Period: last hour, Critical: ${criticalCount}, High: ${highCount}, Total: ${alerts.length}`,
    );

    // TODO: 集成通知系统 → 邮件 / 站内消息 / Webhook
  }

  /**
   * 检查单个用户的告警状态
   */
  async checkUserAlertStatus(
    userId: string,
    region: string,
    tenantId: string,
  ): Promise<{
    hasActiveAlerts: boolean;
    alerts: any[];
    riskScore: number;
  }> {
    // 检查最近7天的异常模式
    const pattern = await this.detectAnomalousPattern(
      userId,
      region,
      tenantId,
      7,
    );

    // 检查连续失败
    const loginFailures = await this.detectContinuousFailures(
      userId,
      AuditAction.LOGIN_FAILED,
      region,
      tenantId,
      30,
    );

    const allAlerts = [
      ...(pattern.detected ? pattern.anomalies : []),
      ...(loginFailures.detected
        ? [
            {
              type: 'LOGIN_FAILURES',
              description: `连续登录失败 ${loginFailures.failureCount} 次`,
              severity: 'HIGH' as const,
            },
          ]
        : []),
    ];

    // 计算风险分数（0-100）
    const riskScore = this.calculateUserRiskScore(allAlerts);

    return {
      hasActiveAlerts: allAlerts.length > 0,
      alerts: allAlerts,
      riskScore,
    };
  }

  /**
   * 计算用户风险分数
   */
  private calculateUserRiskScore(
    alerts: Array<{ severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' }>,
  ): number {
    const weights = {
      CRITICAL: 40,
      HIGH: 25,
      MEDIUM: 10,
      LOW: 5,
    };

    const score = alerts.reduce((sum, alert) => {
      return sum + weights[alert.severity];
    }, 0);

    // 归一化到 0-100
    return Math.min(100, score);
  }

}
