import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  UnauthorizedException,
  ForbiddenException,
  BadRequestException,
  ConflictException,
  HttpStatus,
} from "@nestjs/common";
import type { Request, Response } from "express";
import { Logger } from "nestjs-pino";

/**
 * Standard error response matching domain apiErrorResponseSchema
 */
interface StandardErrorResponse {
  success: false;
  error: {
    code: string;
    message: string;
    details?: Record<string, unknown>;
  };
}

@Catch(UnauthorizedException, ForbiddenException, BadRequestException, ConflictException)
export class AuthErrorFilter implements ExceptionFilter {
  constructor(private readonly logger: Logger) {}

  catch(
    exception: UnauthorizedException | ForbiddenException | BadRequestException | ConflictException,
    host: ArgumentsHost
  ) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const status = exception.getStatus() as HttpStatus;
    const responsePayload = exception.getResponse();
    const payloadMessage =
      typeof responsePayload === "string"
        ? responsePayload
        : Array.isArray((responsePayload as { message?: unknown })?.message)
          ? (responsePayload as { message: unknown[] }).message.find(
              (value): value is string => typeof value === "string"
            )
          : (responsePayload as { message?: unknown })?.message;
    const exceptionMessage = typeof exception.message === "string" ? exception.message : undefined;
    const messageText =
      payloadMessage && typeof payloadMessage === "string"
        ? payloadMessage
        : (exceptionMessage ?? "Authentication error");

    // Map specific auth errors to user-friendly messages
    const userMessage = this.getUserFriendlyMessage(messageText, status);
    const errorCode = this.getErrorCode(messageText, status);

    // Log the error (without sensitive information)
    const userAgentHeader = request.headers["user-agent"];
    const userAgent =
      typeof userAgentHeader === "string"
        ? userAgentHeader
        : Array.isArray(userAgentHeader)
          ? userAgentHeader[0]
          : undefined;
    this.logger.warn("Authentication error", {
      path: request.url,
      method: request.method,
      errorCode,
      userAgent,
      ip: request.ip,
    });

    const errorResponse: StandardErrorResponse = {
      success: false as const,
      error: {
        code: errorCode,
        message: userMessage,
        details: {
          timestamp: new Date().toISOString(),
          path: request.url,
          statusCode: status,
        },
      },
    };

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

  private getUserFriendlyMessage(message: string, status: HttpStatus): string {
    // Production-safe error messages that don't expose sensitive information
    if (status === HttpStatus.UNAUTHORIZED) {
      if (
        message.includes("Invalid credentials") ||
        message.includes("Invalid email or password")
      ) {
        return "Invalid email or password. Please try again.";
      }
      if (message.includes("Token has been revoked") || message.includes("Invalid refresh token")) {
        return "Your session has expired. Please log in again.";
      }
      if (message.includes("Account is locked")) {
        return "Your account has been temporarily locked due to multiple failed login attempts. Please try again later.";
      }
      if (message.includes("Unable to verify credentials")) {
        return "Unable to verify credentials. Please try again later.";
      }
      if (message.includes("Unable to verify account")) {
        return "Unable to verify account. Please try again later.";
      }
      return "Authentication required. Please log in to continue.";
    }

    if (status === HttpStatus.FORBIDDEN) {
      if (message.includes("Admin access required")) {
        return "You do not have permission to access this resource.";
      }
      return "Access denied. You do not have permission to perform this action.";
    }

    if (status === HttpStatus.BAD_REQUEST) {
      if (message.includes("Salesforce account not found")) {
        return "Customer account not found. Please contact support.";
      }
      if (message.includes("Unable to verify customer information")) {
        return "Unable to verify customer information. Please contact support.";
      }
      return "Invalid request. Please check your input and try again.";
    }

    if (status === HttpStatus.CONFLICT) {
      if (message.includes("already linked")) {
        return "This billing account is already linked. Please sign in.";
      }
      if (message.includes("already exists")) {
        return "An account with this email already exists. Please sign in.";
      }
      return "Conflict detected. Please try again.";
    }

    return "Authentication error. Please try again.";
  }

  private getErrorCode(message: string, status: HttpStatus): string {
    if (status === HttpStatus.UNAUTHORIZED) {
      if (message.includes("Invalid credentials") || message.includes("Invalid email or password"))
        return "INVALID_CREDENTIALS";
      if (message.includes("Token has been revoked")) return "TOKEN_REVOKED";
      if (message.includes("Invalid refresh token")) return "INVALID_REFRESH_TOKEN";
      if (message.includes("Account is locked")) return "ACCOUNT_LOCKED";
      if (message.includes("Unable to verify credentials")) return "SERVICE_UNAVAILABLE";
      if (message.includes("Unable to verify account")) return "SERVICE_UNAVAILABLE";
      return "UNAUTHORIZED";
    }

    if (status === HttpStatus.FORBIDDEN) {
      if (message.includes("Admin access required")) return "ADMIN_REQUIRED";
      return "FORBIDDEN";
    }

    if (status === HttpStatus.BAD_REQUEST) {
      if (message.includes("Salesforce account not found")) return "CUSTOMER_NOT_FOUND";
      if (message.includes("Unable to verify customer information")) return "SERVICE_UNAVAILABLE";
      return "INVALID_REQUEST";
    }

    if (status === HttpStatus.CONFLICT) {
      if (message.includes("already linked")) return "ACCOUNT_ALREADY_LINKED";
      if (message.includes("already exists")) return "ACCOUNT_EXISTS";
      return "CONFLICT";
    }

    return "AUTH_ERROR";
  }
}
