import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import { Request, Response } from 'express';

interface RequestLog {
  method: string;
  path: string;
  query: Record<string, unknown>;
  ip: string;
  userAgent: string;
  userId?: string;
}

interface ResponseLog extends RequestLog {
  statusCode: number;
  duration: number;
}

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  private readonly logger = new Logger('HTTP');

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const ctx = context.switchToHttp();
    const request = ctx.getRequest<Request>();
    const response = ctx.getResponse<Response>();

    const startTime = Date.now();
    const requestLog = this.buildRequestLog(request);

    // 记录请求开始
    this.logRequest(requestLog);

    return next.handle().pipe(
      tap(() => {
        const duration = Date.now() - startTime;
        const responseLog: ResponseLog = {
          ...requestLog,
          statusCode: response.statusCode,
          duration,
        };
        this.logResponse(responseLog);
      }),
      catchError((error) => {
        const duration = Date.now() - startTime;
        const responseLog: ResponseLog = {
          ...requestLog,
          statusCode: error.status || 500,
          duration,
        };
        this.logError(responseLog, error);
        throw error;
      }),
    );
  }

  private buildRequestLog(request: Request): RequestLog {
    const user = (request as Request & { user?: { userId?: string } }).user;

    return {
      method: request.method,
      path: request.url,
      query: request.query as Record<string, unknown>,
      ip: this.getClientIp(request),
      userAgent: request.get('user-agent') || 'unknown',
      userId: user?.userId,
    };
  }

  private getClientIp(request: Request): string {
    const forwarded = request.headers['x-forwarded-for'];
    if (typeof forwarded === 'string') {
      return forwarded.split(',')[0].trim();
    }
    if (Array.isArray(forwarded)) {
      return forwarded[0];
    }
    return request.ip || 'unknown';
  }

  private logRequest(log: RequestLog): void {
    const { method, path, ip, userId } = log;
    const userInfo = userId ? `[User: ${userId}]` : '[Anonymous]';

    this.logger.log(`→ ${method} ${path} - ${ip} ${userInfo}`);
  }

  private logResponse(log: ResponseLog): void {
    const { method, path, statusCode, duration } = log;
    const statusEmoji = this.getStatusEmoji(statusCode);

    this.logger.log(`← ${method} ${path} - ${statusCode} ${statusEmoji} - ${duration}ms`);
  }

  private logError(log: ResponseLog, error: Error): void {
    const { method, path, statusCode, duration } = log;

    this.logger.error(
      `← ${method} ${path} - ${statusCode} - ${duration}ms - ${error.message}`,
    );
  }

  private getStatusEmoji(statusCode: number): string {
    if (statusCode >= 200 && statusCode < 300) return 'OK';
    if (statusCode >= 300 && statusCode < 400) return 'REDIRECT';
    if (statusCode >= 400 && statusCode < 500) return 'CLIENT_ERROR';
    return 'SERVER_ERROR';
  }
}
