import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Inject,
} from "@nestjs/common";
import type { Request, Response } from "express";
import { Logger } from "nestjs-pino";
import { SecureErrorMapperService } from "../security/services/secure-error-mapper.service";

@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly secureErrorMapper: SecureErrorMapperService
  ) {}

  catch(exception: unknown, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request & { user?: { id?: string }; requestId?: string }>();

    // Create error context for secure mapping
    const errorContext = {
      userId: request.user?.id,
      requestId: request.requestId || this.generateRequestId(),
      userAgent: request.get("user-agent"),
      ip: request.ip,
      url: request.url,
      method: request.method,
    };

    let status: number;
    let originalError: unknown = exception;

    // Determine HTTP status
    if (exception instanceof HttpException) {
      status = exception.getStatus();

      // Extract the actual error from HttpException response
      const exceptionResponse = exception.getResponse();
      if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
        const errorResponse = exceptionResponse as { message?: string; error?: string };
        originalError = errorResponse.message || exception.message;
      } else {
        originalError =
          typeof exceptionResponse === "string" ? exceptionResponse : exception.message;
      }
    } else {
      status = HttpStatus.INTERNAL_SERVER_ERROR;
      originalError = exception;
    }

    // Use secure error mapper to get safe public message and log securely
    const errorClassification = this.secureErrorMapper.mapError(originalError, errorContext);
    const publicMessage = this.secureErrorMapper.getPublicMessage(originalError, errorContext);

    // Log the error securely (this handles sensitive data filtering)
    this.secureErrorMapper.logSecureError(originalError, errorContext, {
      httpStatus: status,
      exceptionType: exception instanceof Error ? exception.constructor.name : "Unknown",
    });

    // Create secure error response matching domain apiErrorResponseSchema
    const errorResponse = {
      success: false as const,
      error: {
        code: errorClassification.mapping.code,
        message: publicMessage,
        details: {
          statusCode: status,
          category: errorClassification.category.toUpperCase(),
          timestamp: new Date().toISOString(),
          path: request.url,
          requestId: errorContext.requestId,
        },
      },
    };

    // Additional logging for monitoring (without sensitive data)
    this.logger.error(`HTTP ${status} Error [${errorClassification.mapping.code}]`, {
      statusCode: status,
      method: request.method,
      url: request.url,
      userAgent: request.get("user-agent"),
      ip: request.ip,
      errorCode: errorClassification.mapping.code,
      category: errorClassification.category,
      severity: errorClassification.severity,
      requestId: errorContext.requestId,
      userId: errorContext.userId,
    });

    response.status(status).json(errorResponse);
  }

  private generateRequestId(): string {
    return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
  }
}
