/**
 * 超时拦截器
 * 
 * 功能描述：
 * - 实现API接口的超时控制逻辑
 * - 在超时时自动取消请求并返回错误
 * - 记录超时事件的详细日志
 * 
 * 职责分离：
 * - 超时控制逻辑的核心实现
 * - 超时异常的统一处理和响应格式化
 * - 超时事件的日志记录和监控
 * 
 * 主要方法：
 * - intercept() - 拦截器处理入口方法
 * - getTimeoutConfig() - 超时配置获取逻辑
 * - getDefaultTimeoutConfig() - 默认配置提供
 * - isValidTimeoutConfig() - 配置有效性验证
 * 
 * 使用场景：
 * - 全局超时控制
 * - 防止资源泄漏
 * - 提升系统稳定性
 * 
 * 最近修改：
 * - 2026-01-07: 代码规范优化 - 更新注释规范，完善拦截器说明
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2025-12-24
 * @lastModified 2026-01-07
 */

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  RequestTimeoutException,
  Logger
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable, throwError, TimeoutError } from 'rxjs';
import { catchError, timeout } from 'rxjs/operators';
import { TIMEOUT_KEY, TimeoutConfig } from './timeout.decorator';

/**
 * 超时响应接口
 */
interface TimeoutResponse {
  /** 请求是否成功 */
  success: boolean;
  /** 响应消息 */
  message: string;
  /** 错误代码 */
  error_code: string;
  /** 超时信息 */
  timeout_info: {
    /** 超时时间（毫秒） */
    timeout_ms: number;
    /** 超时发生时间 */
    timestamp: string;
  };
}

@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
  private readonly logger = new Logger(TimeoutInterceptor.name);

  constructor(private readonly reflector: Reflector) {}

  /**
   * 拦截器处理函数
   * 
   * 业务逻辑：
   * 1. 获取超时配置
   * 2. 应用超时控制
   * 3. 处理超时异常
   * 4. 记录超时日志
   * 
   * @param context 执行上下文
   * @param next 调用处理器
   * @returns 可观察对象
   */
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // 1. 获取超时配置
    const timeoutConfig = this.getTimeoutConfig(context);
    
    if (!timeoutConfig) {
      // 没有配置超时，直接执行
      return next.handle();
    }

    // 2. 获取请求信息用于日志记录
    const request = context.switchToHttp().getRequest();
    const startTime = Date.now();

    // 3. 应用超时控制
    return next.handle().pipe(
      timeout(timeoutConfig.timeout),
      catchError((error) => {
        if (error instanceof TimeoutError) {
          // 4. 处理超时异常
          const duration = Date.now() - startTime;
          
          // 5. 记录超时日志
          if (timeoutConfig.logTimeout !== false) {
            this.logger.warn('请求超时', {
              operation: 'request_timeout',
              method: request.method,
              url: request.url,
              timeout_ms: timeoutConfig.timeout,
              actual_duration_ms: duration,
              userAgent: request.get('User-Agent'),
              ip: request.ip,
              timestamp: new Date().toISOString()
            });
          }

          // 6. 构建超时响应
          const timeoutResponse: TimeoutResponse = {
            success: false,
            message: timeoutConfig.message || '请求超时，请稍后重试',
            error_code: 'REQUEST_TIMEOUT',
            timeout_info: {
              timeout_ms: timeoutConfig.timeout,
              timestamp: new Date().toISOString()
            }
          };

          // 7. 抛出超时异常
          return throwError(() => new RequestTimeoutException(timeoutResponse));
        }

        // 其他异常直接抛出
        return throwError(() => error);
      })
    );
  }

  /**
   * 获取超时配置
   * 
   * @param context 执行上下文
   * @returns 超时配置或null
   */
  private getTimeoutConfig(context: ExecutionContext): TimeoutConfig | null {
    // 从方法装饰器获取配置
    const methodConfig = this.reflector.get<TimeoutConfig>(
      TIMEOUT_KEY,
      context.getHandler()
    );

    if (methodConfig) {
      return methodConfig;
    }

    // 从类装饰器获取配置
    const classConfig = this.reflector.get<TimeoutConfig>(
      TIMEOUT_KEY,
      context.getClass()
    );

    return classConfig || null;
  }

  /**
   * 获取默认超时配置
   * 
   * @returns 默认超时配置
   */
  private getDefaultTimeoutConfig(): TimeoutConfig {
    return {
      timeout: 30000, // 默认30秒
      message: '请求超时，请稍后重试',
      logTimeout: true
    };
  }

  /**
   * 验证超时配置
   * 
   * @param config 超时配置
   * @returns 是否有效
   */
  private isValidTimeoutConfig(config: TimeoutConfig): boolean {
    return (
      config &&
      typeof config.timeout === 'number' &&
      config.timeout > 0 &&
      config.timeout <= 600000 // 最大10分钟
    );
  }
}