import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
import { RpcException } from '@nestjs/microservices';
import { status as GrpcStatus } from '@grpc/grpc-js';
import { Observable, throwError } from 'rxjs';

function httpToGrpc(code: number): number {
  switch (code) {
    case 400:
      return GrpcStatus.INVALID_ARGUMENT;
    case 401:
      return GrpcStatus.UNAUTHENTICATED;
    case 403:
      return GrpcStatus.PERMISSION_DENIED;
    case 404:
      return GrpcStatus.NOT_FOUND;
    case 409:
      return GrpcStatus.ALREADY_EXISTS;
    case 429:
      return GrpcStatus.RESOURCE_EXHAUSTED;
    default:
      return GrpcStatus.UNKNOWN;
  }
}

@Catch()
export class GrpcExceptionFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost): Observable<never> {
    if (host.getType() !== 'rpc') {
      throw exception;
    }

    let code = GrpcStatus.UNKNOWN;
    let message = 'Внутренняя ошибка сервиса';

    if (exception instanceof HttpException) {
      const response = exception.getResponse();
      const raw = typeof response === 'string' ? response : (response as { message?: string | string[] })?.message ?? exception.message;
      message = Array.isArray(raw) ? raw.join('; ') : String(raw);
      code = httpToGrpc(exception.getStatus());
    } else if (exception instanceof RpcException) {
      const error = exception.getError();
      message = typeof error === 'string' ? error : ((error as { message?: string })?.message ?? message);
    } else if (exception && typeof exception === 'object' && 'message' in exception) {
      message = String((exception as { message: unknown }).message);
    }

    return throwError(() => ({ code, message, details: message }));
  }
}
