import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Inject,
  Optional,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import { Request, Response } from 'express';
import { httpLogger } from '../config/winston.config';
import { LogWriterService, LogEntry } from '../services/log-writer.service';
import { TraceContextService, TraceContext, TRACE_HEADERS } from '../services/trace-context.service';
import { SamplingService } from '../services/sampling.service';
import { AlertService } from '../services/alert.service';
import { StructuredLoggerService } from '../services/structured-logger.service';

/**
 * 日志拦截器
 * 记录所有 HTTP 请求和响应的详细信息
 * 
 * 功能：
 * 1. 同步写入文件（Winston）- 保证日志不丢失
 * 2. 异步写入数据库（LogWriterService）- 支持查询和分析
 * 3. 分布式追踪（TraceContextService）- 跨服务追踪
 * 4. 日志采样（SamplingService）- 高流量控制
 * 5. 实时告警（AlertService）- 慢请求/错误监控
 * 6. 结构化日志（StructuredLoggerService）- ELK 兼容
 */
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  // 可选服务（在 main.ts 中手动创建时可能为空）
  private logWriter: LogWriterService | null = null;
  private traceContextService: TraceContextService | null = null;
  private samplingService: SamplingService | null = null;
  private alertService: AlertService | null = null;
  private structuredLogger: StructuredLoggerService | null = null;

  // 是否启用 JSON 格式输出
  private readonly jsonEnabled: boolean;

  constructor(
    @Optional() @Inject(LogWriterService) logWriter?: LogWriterService,
    @Optional() @Inject(TraceContextService) traceContextService?: TraceContextService,
    @Optional() @Inject(SamplingService) samplingService?: SamplingService,
    @Optional() @Inject(AlertService) alertService?: AlertService,
    @Optional() @Inject(StructuredLoggerService) structuredLogger?: StructuredLoggerService,
  ) {
    this.logWriter = logWriter || null;
    this.traceContextService = traceContextService || null;
    this.samplingService = samplingService || null;
    this.alertService = alertService || null;
    this.structuredLogger = structuredLogger || null;
    this.jsonEnabled = process.env.LOG_JSON_ENABLED === 'true';
  }

  /**
   * 设置服务（用于在 main.ts 中手动注入）
   */
  setServices(services: {
    logWriter?: LogWriterService;
    traceContextService?: TraceContextService;
    samplingService?: SamplingService;
    alertService?: AlertService;
    structuredLogger?: StructuredLoggerService;
  }): void {
    if (services.logWriter) this.logWriter = services.logWriter;
    if (services.traceContextService) this.traceContextService = services.traceContextService;
    if (services.samplingService) this.samplingService = services.samplingService;
    if (services.alertService) this.alertService = services.alertService;
    if (services.structuredLogger) this.structuredLogger = services.structuredLogger;
  }

  // 保持向后兼容
  setLogWriter(logWriter: LogWriterService): void {
    this.logWriter = logWriter;
  }

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const ctx = context.switchToHttp();
    const request = ctx.getRequest<Request>();
    const response = ctx.getResponse<Response>();
    const { method, originalUrl, body, query, params, headers } = request;
    const userAgent = headers['user-agent'] || '';
    const ip = request.ip || request.socket.remoteAddress || '';

    // ⚠️ SSE 请求特殊处理：不设置响应 headers
    const isSSE = request.headers.accept?.includes('text/event-stream');
    
    // 获取用户信息（如果已认证）
    const user = (request as any).user;
    const userId = user?.userId || user?.sub || undefined;
    const username = user?.username || 'anonymous';

    const now = Date.now();

    // 创建或提取追踪上下文
    const traceContext = this.traceContextService
      ? this.traceContextService.extractOrCreate(request)
      : this.createFallbackTraceContext();

    const requestId = this.traceContextService
      ? this.traceContextService.generateRequestId()
      : this.generateRequestId();

    // 将追踪信息注入到响应 Header（SSE 请求跳过）
    if (!isSSE && !response.headersSent) {
      response.setHeader(TRACE_HEADERS.REQUEST_ID, requestId);
      response.setHeader(TRACE_HEADERS.TRACE_ID, traceContext.traceId);
      response.setHeader(TRACE_HEADERS.SPAN_ID, traceContext.spanId);
    }

    // 记录请求到采样服务
    if (this.samplingService) {
      this.samplingService.recordRequest();
    }

    // 检查是否应该记录日志（采样）
    const samplingDecision = this.samplingService
      ? this.samplingService.shouldLog({ level: 'INFO', path: originalUrl })
      : { shouldLog: true, reason: 'always' as const };

    // 记录请求（文件日志）
    if (samplingDecision.shouldLog) {
      this.logRequest({
        requestId,
        traceContext,
        method,
        url: originalUrl,
        userId: userId || 'anonymous',
        username,
        ip,
        userAgent,
        body: this.sanitizeBody(body),
        query,
        params,
      });
    }

    return next.handle().pipe(
      tap((data) => {
        // 记录成功响应
        const responseTime = Date.now() - now;
        if (!response.headersSent) {
          response.setHeader('X-Response-Time', `${responseTime}ms`);
        }

        // 重新检查采样（考虑响应时间）
        const finalSamplingDecision = this.samplingService
          ? this.samplingService.shouldLog({
              level: response.statusCode >= 400 ? 'WARN' : 'INFO',
              path: originalUrl,
              duration: responseTime,
              statusCode: response.statusCode,
            })
          : { shouldLog: true, reason: 'always' as const };

        if (finalSamplingDecision.shouldLog) {
          // 文件日志
          this.logResponse({
            requestId,
            traceContext,
            method,
            url: originalUrl,
            statusCode: response.statusCode,
            responseTime,
            userId: userId || 'anonymous',
            username,
            data: this.sanitizeData(data),
          });
        }

        // 数据库日志（始终记录，用于查询）
        this.writeToDatabase({
          traceId: traceContext.traceId,
          spanId: traceContext.spanId,
          parentSpanId: traceContext.parentSpanId,
          requestId,
          level: response.statusCode >= 400 ? 'WARN' : 'INFO',
          message: `${method} ${originalUrl} | ${response.statusCode} | ${responseTime}ms`,
          method,
          url: originalUrl,
          statusCode: response.statusCode,
          durationMs: responseTime,
          userId,
          userName: username,
          clientIp: ip,
          userAgent,
          service: traceContext.service,
          region: traceContext.region,
          instance: traceContext.instance,
          requestBody: this.sanitizeBody(body),
          responseBody: this.sanitizeData(data),
        });

        // 记录到告警服务
        if (this.alertService) {
          this.alertService.recordRequest({
            duration: responseTime,
            statusCode: response.statusCode,
            path: originalUrl,
            ip,
            traceId: traceContext.traceId,
          });
        }
      }),
      catchError((error) => {
        // 记录错误响应
        const responseTime = Date.now() - now;
        
        // 尝试设置响应时间 Header
        try {
          if (!response.headersSent) {
            response.setHeader('X-Response-Time', `${responseTime}ms`);
          }
        } catch {
          // 忽略 header 设置失败
        }
        
        // 提取错误详情
        const errorDetails: any = {
          name: error.name,
          message: error.message,
          stack: error.stack,
        };
        
        if (error.getResponse && typeof error.getResponse === 'function') {
          errorDetails.response = error.getResponse();
        }
        
        // 文件日志（错误始终记录）
        this.logError({
          requestId,
          traceContext,
          method,
          url: originalUrl,
          statusCode: error.status || 500,
          responseTime,
          userId: userId || 'anonymous',
          username,
          error: errorDetails,
        });

        // 数据库日志
        this.writeToDatabase({
          traceId: traceContext.traceId,
          spanId: traceContext.spanId,
          parentSpanId: traceContext.parentSpanId,
          requestId,
          level: 'ERROR',
          message: `${method} ${originalUrl} | ${error.status || 500} | ${responseTime}ms | ${error.message}`,
          method,
          url: originalUrl,
          statusCode: error.status || 500,
          durationMs: responseTime,
          userId,
          userName: username,
          clientIp: ip,
          userAgent,
          service: traceContext.service,
          region: traceContext.region,
          instance: traceContext.instance,
          errorType: error.name,
          errorMessage: error.message,
          errorCode: errorDetails.response?.code || errorDetails.response?.error?.code,
          errorStack: process.env.NODE_ENV === 'development' ? error.stack : undefined,
          requestBody: this.sanitizeBody(body),
        });

        // 记录到告警服务
        if (this.alertService) {
          this.alertService.recordRequest({
            duration: responseTime,
            statusCode: error.status || 500,
            path: originalUrl,
            ip,
            traceId: traceContext.traceId,
          });
        }

        throw error;
      }),
    );
  }

  /**
   * 写入数据库（异步）
   */
  private writeToDatabase(entry: LogEntry): void {
    if (this.logWriter) {
      try {
        this.logWriter.write(entry);
      } catch (error) {
        httpLogger.warn('Failed to write log to database', { error: (error as Error).message });
      }
    }
  }

  /**
   * 创建回退追踪上下文（当 TraceContextService 不可用时）
   */
  private createFallbackTraceContext(): TraceContext {
    const region = process.env.LOG_REGION || process.env.REGION || 'CN';
    const service = process.env.LOG_SERVICE || 'Backend';
    const timestamp = Date.now();

    return {
      traceId: `${region}-${timestamp}-${this.generateRandom(8)}`,
      spanId: `${service}-${timestamp}-${this.generateRandom(6)}`,
      region,
      service,
      instance: process.env.LOG_INSTANCE || 'local',
    };
  }

  /**
   * 记录请求
   */
  private logRequest(data: {
    requestId: string;
    traceContext: TraceContext;
    method: string;
    url: string;
    userId: string;
    username: string;
    ip?: string;
    userAgent: string;
    body?: any;
    query?: any;
    params?: any;
  }) {
    const { requestId, traceContext, method, url, userId, username, ip, userAgent, body, query, params } = data;

    if (this.jsonEnabled && this.structuredLogger) {
      // JSON 格式输出
      const log = this.structuredLogger.createHttpLog('INFO', `📥 ${method} ${url}`, {
        traceContext,
        requestId,
        method,
        url,
        statusCode: 0,
        durationMs: 0,
        userId: userId !== 'anonymous' ? userId : undefined,
        userName: username,
        clientIp: ip,
        userAgent,
        metadata: { phase: 'request', body, query, params },
      });
      httpLogger.http(this.structuredLogger.toJson(log));
    } else {
      // 人类可读格式
      httpLogger.http(
        `📥 [${requestId}] ${method} ${url} | User: ${username}(${userId}) | IP: ${ip} | Trace: ${traceContext.traceId}`,
      );

      // 详细日志（仅在开发环境或需要调试时）
      if (process.env.NODE_ENV === 'development' || process.env.LOG_LEVEL === 'debug') {
        if (body && Object.keys(body).length > 0) {
          httpLogger.debug(`[${requestId}] Body: ${JSON.stringify(body)}`);
        }
        if (query && Object.keys(query).length > 0) {
          httpLogger.debug(`[${requestId}] Query: ${JSON.stringify(query)}`);
        }
        if (params && Object.keys(params).length > 0) {
          httpLogger.debug(`[${requestId}] Params: ${JSON.stringify(params)}`);
        }
      }
    }
  }

  /**
   * 记录成功响应
   */
  private logResponse(data: {
    requestId: string;
    traceContext: TraceContext;
    method: string;
    url: string;
    statusCode: number;
    responseTime: number;
    userId: string;
    username: string;
    data?: any;
  }) {
    const { requestId, traceContext, method, url, statusCode, responseTime, userId, username, data: responseData } = data;
    const statusEmoji = statusCode >= 200 && statusCode < 300 ? '✅' : '⚠️';

    if (this.jsonEnabled && this.structuredLogger) {
      // JSON 格式输出
      const log = this.structuredLogger.createHttpLog('INFO', `${statusEmoji} ${method} ${url} | ${statusCode} | ${responseTime}ms`, {
        traceContext,
        requestId,
        method,
        url,
        statusCode,
        durationMs: responseTime,
        userId: userId !== 'anonymous' ? userId : undefined,
        userName: username,
      });
      httpLogger.http(this.structuredLogger.toJson(log));
    } else {
      // 人类可读格式
      httpLogger.http(
        `${statusEmoji} [${requestId}] ${method} ${url} | ${statusCode} | ${responseTime}ms | User: ${username}(${userId}) | Trace: ${traceContext.traceId}`,
      );

      // 详细响应日志（仅在开发环境或需要调试时）
      if (process.env.NODE_ENV === 'development' || process.env.LOG_LEVEL === 'debug') {
        if (responseData) {
          const dataString = JSON.stringify(responseData);
          if (dataString.length > 1000) {
            httpLogger.debug(`[${requestId}] Response: ${dataString.substring(0, 1000)}... (truncated)`);
          } else {
            httpLogger.debug(`[${requestId}] Response: ${dataString}`);
          }
        }
      }
    }
  }

  /**
   * 记录错误响应
   */
  private logError(data: {
    requestId: string;
    traceContext: TraceContext;
    method: string;
    url: string;
    statusCode: number;
    responseTime: number;
    userId: string;
    username: string;
    error: {
      name: string;
      message: string;
      stack?: string;
      response?: any;
    };
  }) {
    const { requestId, traceContext, method, url, statusCode, responseTime, userId, username, error } = data;

    if (this.jsonEnabled && this.structuredLogger) {
      // JSON 格式输出
      const log = this.structuredLogger.createHttpLog('ERROR', `❌ ${method} ${url} | ${statusCode} | ${responseTime}ms`, {
        traceContext,
        requestId,
        method,
        url,
        statusCode,
        durationMs: responseTime,
        userId: userId !== 'anonymous' ? userId : undefined,
        userName: username,
        error: {
          type: error.name,
          message: error.message,
          code: error.response?.code || error.response?.error?.code,
          stack: error.stack,
          details: error.response,
        },
      });
      httpLogger.error(this.structuredLogger.toJson(log));
    } else {
      // 人类可读格式
      httpLogger.error(
        `❌ [${requestId}] ${method} ${url} | ${statusCode} | ${responseTime}ms | User: ${username}(${userId}) | Trace: ${traceContext.traceId}`,
      );
      httpLogger.error(`[${requestId}] Error: ${error.name} - ${error.message}`);

      if (error.response) {
        const responseString = typeof error.response === 'object' 
          ? JSON.stringify(error.response) 
          : String(error.response);
        httpLogger.error(`[${requestId}] Error Details: ${responseString}`);
      }

      if (process.env.NODE_ENV === 'development' && error.stack) {
        httpLogger.debug(`[${requestId}] Stack: ${error.stack}`);
      }
    }
  }

  /**
   * 生成请求 ID
   */
  private generateRequestId(): string {
    return `${Date.now()}-${this.generateRandom(7)}`;
  }

  /**
   * 生成随机字符串
   */
  private generateRandom(length: number): string {
    return Math.random().toString(36).substring(2, 2 + length);
  }

  /**
   * 清理敏感数据（请求体）
   */
  private sanitizeBody(body: any): any {
    return this.sanitizeData(body);
  }

  /**
   * 清理敏感数据（响应数据）
   */
  private sanitizeData(data: any, visited = new WeakSet(), depth = 0): any {
    if (typeof data === 'bigint') {
      return data.toString();
    }

    if (!data || typeof data !== 'object') {
      return data;
    }

    if (visited.has(data)) {
      return '[Circular Reference]';
    }

    if (depth > 10) {
      return '[Max Depth Exceeded]';
    }

    visited.add(data);

    if (Array.isArray(data)) {
      return data.map(item => this.sanitizeData(item, visited, depth + 1));
    }

    const sanitized = { ...data };
    const sensitiveFields = [
      'password',
      'passwordHash',
      'token',
      'accessToken',
      'refreshToken',
      'secret',
      'apiKey',
      'authorization',
      'creditCard',
      'idNumber',
    ];

    for (const field of sensitiveFields) {
      if (sanitized[field]) {
        sanitized[field] = '***REDACTED***';
      }
    }

    for (const key in sanitized) {
      if (typeof sanitized[key] === 'bigint') {
        sanitized[key] = sanitized[key].toString();
        continue;
      }
      if (sanitized[key] && typeof sanitized[key] === 'object') {
        sanitized[key] = this.sanitizeData(sanitized[key], visited, depth + 1);
      }
    }

    return sanitized;
  }
}
