import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { Response } from 'express';
import { Prisma } from '../generated/prisma/client.js';
import { MESSAGES } from './messages.js';

/**
 * 全局异常过滤器：统一异常返回结构，并保证 msg 为中文。
 */
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger(HttpExceptionFilter.name);

  catch(exception: unknown, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();

    // 非 HttpException 统一记录堆栈，便于定位 500 根因。
    if (!(exception instanceof HttpException)) {
      const error = exception as { message?: string; stack?: string };
      this.logger.error(error?.message ?? 'Unhandled exception', error?.stack);
    }

    const status = this.resolveStatus(exception);
    const msg = this.resolveMessage(exception, status);

    response.status(status).json({
      code: status,
      msg,
      data: null,
    });
  }

  /**
   * 解析 HTTP 状态码，非 HttpException 统一按 500 处理。
   */
  private resolveStatus(exception: unknown): number {
    if (exception instanceof Prisma.PrismaClientInitializationError) {
      return HttpStatus.SERVICE_UNAVAILABLE;
    }
    if (exception instanceof Prisma.PrismaClientKnownRequestError) {
      switch (exception.code) {
        case 'P2002':
          return HttpStatus.CONFLICT;
        case 'P2025':
          return HttpStatus.NOT_FOUND;
        default:
          return HttpStatus.INTERNAL_SERVER_ERROR;
      }
    }
    if (exception instanceof HttpException) {
      return exception.getStatus();
    }
    return HttpStatus.INTERNAL_SERVER_ERROR;
  }

  /**
   * 解析异常消息：优先使用业务抛出的 message；否则按状态码兜底中文。
   */
  private resolveMessage(exception: unknown, status: number): string {
    if (exception instanceof Prisma.PrismaClientInitializationError) {
      return MESSAGES.DB.CONNECTION_FAILED;
    }
    if (exception instanceof Prisma.PrismaClientKnownRequestError) {
      switch (exception.code) {
        case 'P2021':
          return MESSAGES.DB.TABLE_MISSING;
        case 'P2022':
          return MESSAGES.DB.COLUMN_MISSING;
        case 'P2002':
          return MESSAGES.DEFAULT_CONFLICT;
        case 'P2025':
          return MESSAGES.DEFAULT_NOT_FOUND;
        default:
          return MESSAGES.DEFAULT_INTERNAL_ERROR;
      }
    }

    if (exception instanceof HttpException) {
      const payload = exception.getResponse();
      if (typeof payload === 'string') {
        return payload;
      }
      if (payload && typeof payload === 'object') {
        const body = payload as Record<string, unknown>;
        const message = body.message;
        if (Array.isArray(message)) {
          return message.join('；');
        }
        if (typeof message === 'string' && message.trim()) {
          return message;
        }
      }
    }

    switch (status) {
      case HttpStatus.BAD_REQUEST:
        return MESSAGES.DEFAULT_BAD_REQUEST;
      case HttpStatus.UNAUTHORIZED:
        return MESSAGES.DEFAULT_UNAUTHORIZED;
      case HttpStatus.FORBIDDEN:
        return MESSAGES.DEFAULT_FORBIDDEN;
      case HttpStatus.NOT_FOUND:
        return MESSAGES.DEFAULT_NOT_FOUND;
      case HttpStatus.CONFLICT:
        return MESSAGES.DEFAULT_CONFLICT;
      default:
        return MESSAGES.DEFAULT_INTERNAL_ERROR;
    }
  }
}
