/**
 * 日志采样服务
 * 
 * 在高流量场景下，通过采样策略减少日志量
 * 同时确保关键日志（错误、慢请求、认证）100% 记录
 */

import { Injectable } from '@nestjs/common';
import { winstonLogger } from '../config/winston.config';

// 采样配置接口
export interface SamplingConfig {
  enabled: boolean;
  
  rules: {
    // 永远 100% 记录
    alwaysLog: {
      levels: string[];           // ['ERROR', 'WARN']
      paths: string[];            // ['/api/v1/auth/*']
      conditions: string[];       // ['duration > 1000']
    };
    
    // 永远不记录
    neverLog: {
      paths: string[];            // ['/health', '/ready', '/metrics']
    };
    
    // 动态采样
    dynamic: {
      qpsThreshold: number;       // QPS 超过此值开始采样
      minSampleRate: number;      // 最低采样率 10%
      maxSampleRate: number;      // 最高采样率 100%
    };
  };
}

// 采样决策结果
export interface SamplingDecision {
  shouldLog: boolean;
  reason: 'always' | 'never' | 'sampled' | 'not_sampled';
  sampleRate?: number;
}

// QPS 统计窗口
interface QpsWindow {
  count: number;
  startTime: number;
}

@Injectable()
export class SamplingService {
  private config: SamplingConfig;
  private qpsWindow: QpsWindow = { count: 0, startTime: Date.now() };
  private currentQps: number = 0;

  constructor() {
    // 默认配置
    this.config = {
      enabled: process.env.LOG_SAMPLING_ENABLED === 'true',
      rules: {
        alwaysLog: {
          levels: ['ERROR', 'WARN'],
          paths: ['/api/v1/auth/*', '/api/v1/login', '/api/v1/logout'],
          conditions: ['duration > 1000'],
        },
        neverLog: {
          paths: ['/health', '/ready', '/metrics', '/api/v1/health'],
        },
        dynamic: {
          qpsThreshold: parseInt(process.env.LOG_SAMPLING_QPS_THRESHOLD || '1000', 10),
          minSampleRate: parseFloat(process.env.LOG_SAMPLING_MIN_RATE || '0.1'),
          maxSampleRate: parseFloat(process.env.LOG_SAMPLING_MAX_RATE || '1.0'),
        },
      },
    };

    // 每秒更新 QPS
    setInterval(() => this.updateQps(), 1000);
  }

  /**
   * 判断是否应该记录日志
   */
  shouldLog(context: {
    level: string;
    path: string;
    duration?: number;
    statusCode?: number;
  }): SamplingDecision {
    // 如果采样未启用，全部记录
    if (!this.config.enabled) {
      return { shouldLog: true, reason: 'always' };
    }

    const { level, path, duration, statusCode } = context;

    // 1. 检查永不记录规则
    if (this.matchesNeverLog(path)) {
      return { shouldLog: false, reason: 'never' };
    }

    // 2. 检查永远记录规则
    if (this.matchesAlwaysLog(level, path, duration, statusCode)) {
      return { shouldLog: true, reason: 'always' };
    }

    // 3. 动态采样
    const sampleRate = this.calculateSampleRate();
    const shouldLog = Math.random() < sampleRate;

    return {
      shouldLog,
      reason: shouldLog ? 'sampled' : 'not_sampled',
      sampleRate,
    };
  }

  /**
   * 检查是否匹配永不记录规则
   */
  private matchesNeverLog(path: string): boolean {
    return this.config.rules.neverLog.paths.some(pattern => 
      this.matchPath(path, pattern)
    );
  }

  /**
   * 检查是否匹配永远记录规则
   */
  private matchesAlwaysLog(
    level: string,
    path: string,
    duration?: number,
    statusCode?: number,
  ): boolean {
    const { alwaysLog } = this.config.rules;

    // 检查日志级别
    if (alwaysLog.levels.includes(level.toUpperCase())) {
      return true;
    }

    // 检查路径
    if (alwaysLog.paths.some(pattern => this.matchPath(path, pattern))) {
      return true;
    }

    // 检查条件
    for (const condition of alwaysLog.conditions) {
      if (this.evaluateCondition(condition, { duration, statusCode })) {
        return true;
      }
    }

    // 错误状态码永远记录
    if (statusCode && statusCode >= 500) {
      return true;
    }

    return false;
  }

  /**
   * 计算当前采样率
   * 根据 QPS 动态调整
   */
  private calculateSampleRate(): number {
    const { dynamic } = this.config.rules;

    if (this.currentQps <= dynamic.qpsThreshold) {
      return dynamic.maxSampleRate;
    }

    // 线性降低采样率
    // QPS 越高，采样率越低
    const ratio = dynamic.qpsThreshold / this.currentQps;
    const rate = Math.max(dynamic.minSampleRate, ratio * dynamic.maxSampleRate);

    return rate;
  }

  /**
   * 更新 QPS 统计
   */
  private updateQps(): void {
    const now = Date.now();
    const elapsed = (now - this.qpsWindow.startTime) / 1000;

    if (elapsed >= 1) {
      this.currentQps = this.qpsWindow.count / elapsed;
      this.qpsWindow = { count: 0, startTime: now };
    }
  }

  /**
   * 记录一次请求（用于 QPS 统计）
   */
  recordRequest(): void {
    this.qpsWindow.count++;
  }

  /**
   * 路径匹配（支持通配符 *）
   */
  private matchPath(path: string, pattern: string): boolean {
    // 简单的通配符匹配
    const regexPattern = pattern
      .replace(/\*/g, '.*')
      .replace(/\//g, '\\/');
    const regex = new RegExp(`^${regexPattern}$`);
    return regex.test(path);
  }

  /**
   * 评估条件表达式
   */
  private evaluateCondition(
    condition: string,
    context: { duration?: number; statusCode?: number },
  ): boolean {
    // 简单的条件解析
    // 支持: duration > 1000, statusCode >= 500
    const match = condition.match(/(\w+)\s*(>|>=|<|<=|==)\s*(\d+)/);
    if (!match) return false;

    const [, field, operator, valueStr] = match;
    const value = parseInt(valueStr, 10);
    const contextValue = context[field as keyof typeof context];

    if (contextValue === undefined) return false;

    switch (operator) {
      case '>': return contextValue > value;
      case '>=': return contextValue >= value;
      case '<': return contextValue < value;
      case '<=': return contextValue <= value;
      case '==': return contextValue === value;
      default: return false;
    }
  }

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

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

  /**
   * 获取当前 QPS
   */
  getCurrentQps(): number {
    return this.currentQps;
  }

  /**
   * 获取当前采样率
   */
  getCurrentSampleRate(): number {
    return this.calculateSampleRate();
  }

  /**
   * 获取采样统计
   */
  getStats(): {
    enabled: boolean;
    currentQps: number;
    currentSampleRate: number;
    qpsThreshold: number;
  } {
    return {
      enabled: this.config.enabled,
      currentQps: this.currentQps,
      currentSampleRate: this.calculateSampleRate(),
      qpsThreshold: this.config.rules.dynamic.qpsThreshold,
    };
  }
}

