/**
 * 日志告警服务
 * 
 * 监控慢请求、高错误率、磁盘空间等指标
 * 当超过阈值时发送告警通知
 */

import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { LogAlertType, LogAlertSeverity, LogAlertStatus } from '@prisma/client';
import { winstonLogger } from '../config/winston.config';
import * as fs from 'fs';
import * as path from 'path';

// 告警服务内部配置接口
export interface AlertServiceConfig {
  slowRequest: {
    enabled: boolean;
    thresholdMs: number;          // 默认 2000ms
    excludePaths: string[];       // 排除的路径
  };
  
  errorRate: {
    enabled: boolean;
    thresholdPercent: number;     // 默认 5%
    windowMinutes: number;        // 统计窗口，默认 5 分钟
    minRequests: number;          // 最小请求数，默认 100
  };
  
  ipAnomaly: {
    enabled: boolean;
    maxRequestsPerMinute: number; // 默认 100
    blockDurationMinutes: number; // 阻止时长，默认 10 分钟
  };
  
  diskSpace: {
    enabled: boolean;
    warningPercent: number;       // 警告阈值，默认 80%
    criticalPercent: number;      // 严重阈值，默认 90%
    logDir: string;               // 日志目录
  };

  notifications: {
    slack?: { enabled: boolean; webhookUrl?: string };
    feishu?: { enabled: boolean; webhookUrl?: string };
    email?: { enabled: boolean; recipients?: string[] };
  };
}

// 告警上下文
export interface AlertContext {
  type: LogAlertType;
  severity: LogAlertSeverity;
  message: string;
  details: Record<string, any>;
  traceId?: string;
}

// 统计窗口
interface StatsWindow {
  totalRequests: number;
  errorCount: number;
  slowCount: number;
  startTime: number;
  ipCounts: Map<string, number>;
}

@Injectable()
export class AlertService implements OnModuleInit, OnModuleDestroy {
  private config: AlertServiceConfig;
  private statsWindow: StatsWindow;
  private checkInterval: NodeJS.Timeout | null = null;
  private alertCooldown: Map<string, number> = new Map(); // 告警冷却，避免重复告警

  // 告警冷却时间（毫秒）
  private readonly ALERT_COOLDOWN_MS = 5 * 60 * 1000; // 5 分钟

  constructor(private readonly prisma: PrismaService) {
    // 默认配置
    this.config = {
      slowRequest: {
        enabled: true,
        thresholdMs: parseInt(process.env.LOG_ALERT_SLOW_THRESHOLD_MS || '2000', 10),
        excludePaths: ['/api/v1/files/upload', '/api/v1/export'],
      },
      errorRate: {
        enabled: true,
        thresholdPercent: parseFloat(process.env.LOG_ALERT_ERROR_RATE_PERCENT || '5'),
        windowMinutes: parseInt(process.env.LOG_ALERT_ERROR_RATE_WINDOW || '5', 10),
        minRequests: parseInt(process.env.LOG_ALERT_ERROR_RATE_MIN_REQUESTS || '100', 10),
      },
      ipAnomaly: {
        enabled: true,
        maxRequestsPerMinute: parseInt(process.env.LOG_ALERT_IP_MAX_RPM || '100', 10),
        blockDurationMinutes: parseInt(process.env.LOG_ALERT_IP_BLOCK_DURATION || '10', 10),
      },
      diskSpace: {
        enabled: true,
        warningPercent: parseInt(process.env.LOG_ALERT_DISK_WARNING || '80', 10),
        criticalPercent: parseInt(process.env.LOG_ALERT_DISK_CRITICAL || '90', 10),
        logDir: process.env.LOG_DIR || path.join(process.cwd(), 'logs'),
      },
      notifications: {
        slack: {
          enabled: !!process.env.LOG_ALERT_SLACK_WEBHOOK,
          webhookUrl: process.env.LOG_ALERT_SLACK_WEBHOOK,
        },
        feishu: {
          enabled: !!process.env.LOG_ALERT_FEISHU_WEBHOOK,
          webhookUrl: process.env.LOG_ALERT_FEISHU_WEBHOOK,
        },
        email: {
          enabled: !!process.env.LOG_ALERT_EMAIL_RECIPIENTS,
          recipients: process.env.LOG_ALERT_EMAIL_RECIPIENTS?.split(','),
        },
      },
    };

    this.resetStatsWindow();
  }

  onModuleInit() {
    // 每分钟检查一次告警条件
    this.checkInterval = setInterval(() => {
      this.checkAlerts().catch(err => {
        winstonLogger.error('Alert check failed', { error: err.message });
      });
    }, 60 * 1000);

    winstonLogger.info('AlertService initialized', {
      slowRequestThreshold: this.config.slowRequest.thresholdMs,
      errorRateThreshold: this.config.errorRate.thresholdPercent,
    });
  }

  onModuleDestroy() {
    if (this.checkInterval) {
      clearInterval(this.checkInterval);
      this.checkInterval = null;
    }
  }

  /**
   * 记录请求（用于统计）
   */
  recordRequest(context: {
    duration: number;
    statusCode: number;
    path: string;
    ip: string;
    traceId?: string;
  }): void {
    const { duration, statusCode, path: reqPath, ip, traceId } = context;

    this.statsWindow.totalRequests++;

    // 统计错误
    if (statusCode >= 500) {
      this.statsWindow.errorCount++;
    }

    // 统计 IP 请求数
    const currentIpCount = this.statsWindow.ipCounts.get(ip) || 0;
    this.statsWindow.ipCounts.set(ip, currentIpCount + 1);

    // 检查慢请求（实时告警）
    if (this.config.slowRequest.enabled && duration > this.config.slowRequest.thresholdMs) {
      // 检查是否在排除路径中
      const isExcluded = this.config.slowRequest.excludePaths.some(p => 
        reqPath.startsWith(p) || reqPath.includes(p)
      );

      if (!isExcluded) {
        this.statsWindow.slowCount++;
        this.triggerSlowRequestAlert(reqPath, duration, traceId);
      }
    }
  }

  /**
   * 检查所有告警条件
   */
  private async checkAlerts(): Promise<void> {
    const now = Date.now();
    const windowDuration = (now - this.statsWindow.startTime) / 1000 / 60; // 分钟

    // 检查错误率
    if (this.config.errorRate.enabled && windowDuration >= this.config.errorRate.windowMinutes) {
      await this.checkErrorRate();
    }

    // 检查 IP 异常
    if (this.config.ipAnomaly.enabled) {
      await this.checkIpAnomaly();
    }

    // 检查磁盘空间
    if (this.config.diskSpace.enabled) {
      await this.checkDiskSpace();
    }

    // 重置统计窗口
    if (windowDuration >= this.config.errorRate.windowMinutes) {
      this.resetStatsWindow();
    }
  }

  /**
   * 检查错误率
   */
  private async checkErrorRate(): Promise<void> {
    const { totalRequests, errorCount } = this.statsWindow;
    const { thresholdPercent, minRequests } = this.config.errorRate;

    if (totalRequests < minRequests) {
      return; // 请求数不足，不检查
    }

    const errorRate = (errorCount / totalRequests) * 100;

    if (errorRate > thresholdPercent) {
      await this.triggerAlert({
        type: LogAlertType.HIGH_ERROR_RATE,
        severity: errorRate > thresholdPercent * 2 ? LogAlertSeverity.CRITICAL : LogAlertSeverity.WARNING,
        message: `高错误率告警: ${errorRate.toFixed(2)}% (阈值: ${thresholdPercent}%)`,
        details: {
          errorRate: errorRate.toFixed(2),
          threshold: thresholdPercent,
          totalRequests,
          errorCount,
          windowMinutes: this.config.errorRate.windowMinutes,
        },
      });
    }
  }

  /**
   * 检查 IP 异常
   */
  private async checkIpAnomaly(): Promise<void> {
    const { maxRequestsPerMinute } = this.config.ipAnomaly;

    for (const [ip, count] of this.statsWindow.ipCounts) {
      if (count > maxRequestsPerMinute) {
        await this.triggerAlert({
          type: LogAlertType.IP_ANOMALY,
          severity: count > maxRequestsPerMinute * 3 ? LogAlertSeverity.CRITICAL : LogAlertSeverity.WARNING,
          message: `IP 异常告警: ${ip} 请求 ${count} 次/分钟 (阈值: ${maxRequestsPerMinute})`,
          details: {
            ip,
            requestCount: count,
            threshold: maxRequestsPerMinute,
          },
        });
      }
    }
  }

  /**
   * 检查磁盘空间
   */
  private async checkDiskSpace(): Promise<void> {
    try {
      const { logDir, warningPercent, criticalPercent } = this.config.diskSpace;

      // 检查日志目录是否存在
      if (!fs.existsSync(logDir)) {
        return;
      }

      // 获取磁盘使用情况（简化实现）
      const stats = fs.statfsSync(logDir);
      const totalBytes = stats.blocks * stats.bsize;
      const freeBytes = stats.bfree * stats.bsize;
      const usedPercent = ((totalBytes - freeBytes) / totalBytes) * 100;

      if (usedPercent >= criticalPercent) {
        await this.triggerAlert({
          type: LogAlertType.DISK_SPACE,
          severity: LogAlertSeverity.CRITICAL,
          message: `磁盘空间严重不足: ${usedPercent.toFixed(1)}% (阈值: ${criticalPercent}%)`,
          details: {
            usedPercent: usedPercent.toFixed(1),
            threshold: criticalPercent,
            totalGB: (totalBytes / 1024 / 1024 / 1024).toFixed(2),
            freeGB: (freeBytes / 1024 / 1024 / 1024).toFixed(2),
            logDir,
          },
        });
      } else if (usedPercent >= warningPercent) {
        await this.triggerAlert({
          type: LogAlertType.DISK_SPACE,
          severity: LogAlertSeverity.WARNING,
          message: `磁盘空间告警: ${usedPercent.toFixed(1)}% (阈值: ${warningPercent}%)`,
          details: {
            usedPercent: usedPercent.toFixed(1),
            threshold: warningPercent,
            totalGB: (totalBytes / 1024 / 1024 / 1024).toFixed(2),
            freeGB: (freeBytes / 1024 / 1024 / 1024).toFixed(2),
            logDir,
          },
        });
      }
    } catch (error) {
      // 某些系统可能不支持 statfsSync
      winstonLogger.debug('Disk space check failed', { error: (error as Error).message });
    }
  }

  /**
   * 触发慢请求告警
   */
  private async triggerSlowRequestAlert(path: string, duration: number, traceId?: string): Promise<void> {
    await this.triggerAlert({
      type: LogAlertType.SLOW_REQUEST,
      severity: duration > this.config.slowRequest.thresholdMs * 2 
        ? LogAlertSeverity.CRITICAL 
        : LogAlertSeverity.WARNING,
      message: `慢请求告警: ${path} 耗时 ${duration}ms (阈值: ${this.config.slowRequest.thresholdMs}ms)`,
      details: {
        path,
        duration,
        threshold: this.config.slowRequest.thresholdMs,
      },
      traceId,
    });
  }

  /**
   * 触发告警
   */
  private async triggerAlert(context: AlertContext): Promise<void> {
    const { type, severity, message, details, traceId } = context;

    // 检查冷却时间
    const alertKey = `${type}-${JSON.stringify(details)}`;
    const lastAlertTime = this.alertCooldown.get(alertKey);
    if (lastAlertTime && Date.now() - lastAlertTime < this.ALERT_COOLDOWN_MS) {
      return; // 在冷却期内，不重复告警
    }

    // 更新冷却时间
    this.alertCooldown.set(alertKey, Date.now());

    // 记录到日志
    winstonLogger.warn(`🚨 [ALERT] ${message}`, { type, severity, details, traceId });

    // 保存到数据库
    try {
      await this.prisma.logAlert.create({
        data: {
          type,
          severity,
          message,
          context: details,
          channels: this.getEnabledChannels(),
          status: LogAlertStatus.SENT,
          traceId,
        },
      });
    } catch (error) {
      winstonLogger.error('Failed to save alert to database', { error: (error as Error).message });
    }

    // 发送通知
    await this.sendNotifications(context);
  }

  /**
   * 发送通知
   */
  private async sendNotifications(context: AlertContext): Promise<void> {
    const { notifications } = this.config;
    const { type, severity, message, details, traceId } = context;

    // 构建通知内容
    const notificationContent = this.buildNotificationContent(context);

    // Slack 通知
    if (notifications.slack?.enabled && notifications.slack.webhookUrl) {
      await this.sendSlackNotification(notifications.slack.webhookUrl, notificationContent);
    }

    // 飞书通知
    if (notifications.feishu?.enabled && notifications.feishu.webhookUrl) {
      await this.sendFeishuNotification(notifications.feishu.webhookUrl, notificationContent);
    }

    // Email 通知（需要集成邮件服务）
    // if (notifications.email?.enabled && notifications.email.recipients) {
    //   await this.sendEmailNotification(notifications.email.recipients, notificationContent);
    // }
  }

  /**
   * 构建通知内容
   */
  private buildNotificationContent(context: AlertContext): string {
    const { type, severity, message, details, traceId } = context;
    const region = process.env.LOG_REGION || 'CN';
    const service = process.env.LOG_SERVICE || 'Backend';

    const severityEmoji = severity === LogAlertSeverity.CRITICAL ? '🔴' : severity === LogAlertSeverity.WARNING ? '🟡' : '🔵';

    let content = `${severityEmoji} [${type}] ${region} Region\n`;
    content += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
    content += `${message}\n`;
    content += `Service: ${service}\n`;
    
    if (traceId) {
      content += `TraceId: ${traceId}\n`;
    }

    content += `Time: ${new Date().toISOString()}\n`;
    content += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;

    // 添加详情
    for (const [key, value] of Object.entries(details)) {
      content += `${key}: ${value}\n`;
    }

    return content;
  }

  /**
   * 发送 Slack 通知
   */
  private async sendSlackNotification(webhookUrl: string, content: string): Promise<void> {
    try {
      const response = await fetch(webhookUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ text: content }),
      });

      if (!response.ok) {
        winstonLogger.error('Slack notification failed', { status: response.status });
      }
    } catch (error) {
      winstonLogger.error('Slack notification error', { error: (error as Error).message });
    }
  }

  /**
   * 发送飞书通知
   */
  private async sendFeishuNotification(webhookUrl: string, content: string): Promise<void> {
    try {
      const response = await fetch(webhookUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          msg_type: 'text',
          content: { text: content },
        }),
      });

      if (!response.ok) {
        winstonLogger.error('Feishu notification failed', { status: response.status });
      }
    } catch (error) {
      winstonLogger.error('Feishu notification error', { error: (error as Error).message });
    }
  }

  /**
   * 获取启用的通知渠道
   */
  private getEnabledChannels(): string[] {
    const channels: string[] = [];
    const { notifications } = this.config;

    if (notifications.slack?.enabled) channels.push('slack');
    if (notifications.feishu?.enabled) channels.push('feishu');
    if (notifications.email?.enabled) channels.push('email');

    return channels;
  }

  /**
   * 重置统计窗口
   */
  private resetStatsWindow(): void {
    this.statsWindow = {
      totalRequests: 0,
      errorCount: 0,
      slowCount: 0,
      startTime: Date.now(),
      ipCounts: new Map(),
    };
  }

  /**
   * 获取配置
   */
  getConfig(): AlertServiceConfig {
    return { ...this.config };
  }

  /**
   * 更新配置
   */
  updateConfig(config: Partial<AlertServiceConfig>): void {
    this.config = { ...this.config, ...config };
    winstonLogger.info('Alert config updated', { config: this.config });
  }

  /**
   * 获取统计信息
   */
  getStats(): {
    windowStartTime: string;
    totalRequests: number;
    errorCount: number;
    slowCount: number;
    errorRate: number;
    topIps: { ip: string; count: number }[];
  } {
    const errorRate = this.statsWindow.totalRequests > 0
      ? (this.statsWindow.errorCount / this.statsWindow.totalRequests) * 100
      : 0;

    // 获取 Top 10 IP
    const topIps = Array.from(this.statsWindow.ipCounts.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, 10)
      .map(([ip, count]) => ({ ip, count }));

    return {
      windowStartTime: new Date(this.statsWindow.startTime).toISOString(),
      totalRequests: this.statsWindow.totalRequests,
      errorCount: this.statsWindow.errorCount,
      slowCount: this.statsWindow.slowCount,
      errorRate: parseFloat(errorRate.toFixed(2)),
      topIps,
    };
  }
}

