import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Logger,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import { Reflector } from '@nestjs/core';
import { AuditService } from '../audit.service';
import { AuditAction, AuditStatus, RiskLevel, ComplianceLevel } from '@prisma/client';
import {
  AUDITABLE_KEY,
  SENSITIVE_KEY,
  FINANCIAL_KEY,
} from '../decorators/auditable.decorator';
import { randomUUID as uuidv4 } from 'crypto';
import { getClientIp } from '@common/utils/client-ip';

/**
 * 审计日志拦截器
 * 自动拦截 HTTP 请求并记录审计日志
 */
@Injectable()
export class AuditLogInterceptor implements NestInterceptor {
  private readonly logger = new Logger(AuditLogInterceptor.name);

  constructor(
    private readonly auditService: AuditService,
    private readonly reflector: Reflector,
  ) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const handler = context.getHandler();
    const controller = context.getClass();

    // 检查是否需要审计
    const isAuditable = this.reflector.getAllAndOverride<boolean>(
      AUDITABLE_KEY,
      [handler, controller],
    );

    // ✅ 调试日志（临时，用于排查问题）
    if (request.url.includes('/departments/') && request.method !== 'GET') {
      this.logger.log(`🔍 [Audit Check] ${request.method} ${request.url} - Auditable: ${isAuditable}`);
    }

    // 如果不需要审计，直接放行
    if (!isAuditable) {
      return next.handle();
    }

    // ✅ 审计将被记录
    this.logger.log(`📝 [Audit] Recording audit for ${request.method} ${request.url}`);

    // 检查是否是敏感操作
    const isSensitive = this.reflector.getAllAndOverride<boolean>(
      SENSITIVE_KEY,
      [handler, controller],
    );

    // 检查是否是财务操作
    const isFinancial = this.reflector.getAllAndOverride<boolean>(
      FINANCIAL_KEY,
      [handler, controller],
    );

    const startTime = Date.now();
    const traceId = request.headers['x-trace-id'] || this.generateTraceId();
    const user = request.user;

    // ✅ 调试：查看用户信息
    this.logger.log(`👤 [Audit] User info: ${JSON.stringify({ 
      userId: user?.userId,   // ✅ 正确的字段名
      username: user?.username,
      hasUser: !!user 
    })}`);

    // 获取多租户信息
    const region = (user?.region || user?.defaultRegion || request.headers['x-region'] || 'cn').toLowerCase();
    const tenantId = user?.tenantId || request.headers['x-tenant-id'] || 'default';

    // 确定风险级别和合规级别
    const riskLevel = this.determineRiskLevel(request, isSensitive, isFinancial);
    const complianceLevel = this.determineComplianceLevel(riskLevel, isFinancial);

    // 提取请求信息
    const auditData = {
      // 多租户
      region,
      tenantId,

      // 5W1H
      who: user?.username || user?.displayName || 'anonymous',
      what: `${request.method} ${request.url}`,
      where: this.getClientIp(request),
      how: 'HTTP_API',

      // 操作详情
      module: controller.name.replace('Controller', ''),
      action: this.mapMethodToAction(request.method),
      entityType: this.extractEntityType(request),
      entityId: this.extractEntityId(request),

      // 变更内容
      newValue: this.sanitizeBody(request.body),

      // 上下文
      userId: user?.userId || null, // 匿名操作时为 null
      sessionId:
        request.session?.id || request.headers['x-session-id'] || uuidv4(),
      traceId,
      requestId: request.id || this.generateRequestId(),

      // 环境
      ipAddress: this.getClientIp(request),
      userAgent: request.headers['user-agent'] || 'unknown',
      deviceId: request.headers['x-device-id'] || null, // 允许为 null
      geoLocation: request.headers['x-geo-location'],

      // 业务
      businessType: request.headers['x-business-type'],
      businessKey: request.headers['x-business-key'],

      // 合规
      isSensitive: isSensitive || false,
      isFinancial: isFinancial || this.isFinancialOperation(request),
      riskLevel,
      complianceLevel,
    };

    return next.handle().pipe(
      tap(() => {
        // 成功的情况
        const duration = Date.now() - startTime;

        // ✅ 准备写入审计日志
        this.logger.log(`💾 [Audit] Logging SUCCESS for ${request.method} ${request.url}, userId: ${auditData.userId}`);

        // 异步记录日志，不阻塞响应
        this.auditService
          .log({
            ...auditData,
            status: AuditStatus.SUCCESS,
            duration,
          })
          .then(() => {
            this.logger.log(`[Audit] ✅ Logged: ${auditData.module}.${auditData.action}`);
          })
          .catch((err) => {
            this.logger.error(`[Audit] ❌ Failed to log audit: ${err.message}`, err.stack);
          });
      }),
      catchError((error) => {
        // 失败的情况
        const duration = Date.now() - startTime;

        // 异步记录日志
        this.auditService
          .log({
            ...auditData,
            status: AuditStatus.FAILED,
            errorMessage: error.message || error.toString(),
            duration,
          })
          .catch((err) => {
            this.logger.error('Failed to log audit on error', err);
          });

        return throwError(() => error);
      }),
    );
  }

  /**
   * 确定风险级别
   */
  private determineRiskLevel(
    request: any,
    isSensitive: boolean,
    isFinancial: boolean,
  ): RiskLevel {
    // 财务操作和敏感操作为高风险
    if (isFinancial || isSensitive) {
      return RiskLevel.HIGH;
    }

    // 删除操作为高风险
    if (request.method === 'DELETE') {
      return RiskLevel.HIGH;
    }

    // 批量操作为中风险
    if (
      request.url.includes('bulk') ||
      request.url.includes('batch') ||
      Array.isArray(request.body)
    ) {
      return RiskLevel.MEDIUM;
    }

    // 创建和更新为中风险
    if (request.method === 'POST' || request.method === 'PUT' || request.method === 'PATCH') {
      return RiskLevel.MEDIUM;
    }

    // 读取操作为低风险
    return RiskLevel.LOW;
  }

  /**
   * 确定合规级别（决定保留年限）
   */
  private determineComplianceLevel(
    riskLevel: RiskLevel,
    isFinancial: boolean,
  ): ComplianceLevel {
    // 财务操作强制 HIGH
    if (isFinancial) {
      return ComplianceLevel.HIGH;
    }

    // 根据风险级别映射
    switch (riskLevel) {
      case RiskLevel.HIGH:
        return ComplianceLevel.HIGH;
      case RiskLevel.MEDIUM:
        return ComplianceLevel.MEDIUM;
      case RiskLevel.LOW:
        return ComplianceLevel.LOW;
      default:
        return ComplianceLevel.MEDIUM;
    }
  }

  private generateTraceId(): string {
    return uuidv4();
  }

  private generateRequestId(): string {
    return uuidv4();
  }

  private getClientIp(request: any): string {
    // 复用 common/utils/client-ip.ts，保持全局 IP 提取逻辑一致
    // 拦截器传 `any`（非 typed Express.Request），但 helper 的字段访问安全（可选链）
    return getClientIp(request);
  }

  private mapMethodToAction(method: string): AuditAction {
    const mapping: Record<string, AuditAction> = {
      POST: AuditAction.CREATE,
      GET: AuditAction.READ,
      PUT: AuditAction.UPDATE,
      PATCH: AuditAction.UPDATE,
      DELETE: AuditAction.DELETE,
    };
    return mapping[method] || AuditAction.READ;
  }

  private extractEntityType(request: any): string {
    // 从 URL 提取实体类型: /api/v1/users/123 -> users
    const match = request.url.match(/\/api\/v\d+\/([^\/\?]+)/);
    return match ? match[1] : 'unknown';
  }

  private extractEntityId(request: any): string {
    // 从 URL 或 body 提取实体ID
    // 匹配 UUID 格式
    const uuidMatch = request.url.match(
      /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i,
    );
    if (uuidMatch) return uuidMatch[1];

    // 匹配数字ID（转换为字符串形式的 UUID）
    const numMatch = request.url.match(/\/(\d+)(?:\/|$|\?)/);
    if (numMatch) return numMatch[1];

    // 从 body 或 params 提取
    const id = request.body?.id || request.params?.id;
    // 如果有 ID 且是 UUID 格式则返回，否则生成新的 UUID
    if (id && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(id)) {
      return id;
    }
    return uuidv4(); // 未知实体使用临时 UUID
  }

  private isFinancialOperation(request: any): boolean {
    // 判断是否是财务相关操作
    const financialKeywords = [
      'payment',
      'invoice',
      'expense',
      'budget',
      'salary',
      'refund',
      'financial',
      'billing',
      'transaction',
    ];
    const url = request.url.toLowerCase();
    return financialKeywords.some((keyword) => url.includes(keyword));
  }

  private sanitizeBody(body: any): any {
    if (!body) return null;

    // 移除敏感字段
    const sensitiveFields = [
      'password',
      'passwordHash',
      'token',
      'accessToken',
      'refreshToken',
      'secret',
      'apiKey',
      'privateKey',
      'creditCard',
      'ssn',
      'idCard',
      'bankCard',
      'cvv',
    ];

    const sanitize = (obj: any): any => {
      if (typeof obj !== 'object' || obj === null) return obj;

      if (Array.isArray(obj)) {
        return obj.map((item) => sanitize(item));
      }

      const result: any = {};
      for (const [key, value] of Object.entries(obj)) {
        const lowerKey = key.toLowerCase();
        if (sensitiveFields.some((f) => lowerKey.includes(f.toLowerCase()))) {
          result[key] = '***REDACTED***';
        } else if (typeof value === 'object') {
          result[key] = sanitize(value);
        } else {
          result[key] = value;
        }
      }
      return result;
    };

    return sanitize(body);
  }
}
