import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';
import {
  formatApiErrorMessage,
  isApiErrorCode,
  type ApiErrorCode,
  type ApiErrorParams,
} from '@thebet365/shared';
import { serializeBigInt } from './decorators';
import { isCodedExceptionResponse } from './app-error';
import { resolveRequestLocale } from './resolve-request-locale';

@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const locale = resolveRequestLocale(request);

    let status = HttpStatus.INTERNAL_SERVER_ERROR;
    let code: ApiErrorCode = 'INTERNAL_SERVER_ERROR';
    let params: ApiErrorParams | undefined;
    let extraData: unknown = null;
    let message = formatApiErrorMessage('INTERNAL_SERVER_ERROR', locale);

    if (exception instanceof HttpException) {
      status = exception.getStatus();
      const res = exception.getResponse();

      if (isCodedExceptionResponse(res) && isApiErrorCode(res.code)) {
        code = res.code;
        params = res.params;
        message = formatApiErrorMessage(code, locale, params);
        if (typeof res === 'object' && res !== null && 'data' in res) {
          extraData = (res as { data?: unknown }).data ?? null;
        }
      } else if (typeof res === 'string') {
        message = res;
      } else if (typeof res === 'object' && res !== null) {
        const body = res as { message?: string | string[]; code?: string; params?: ApiErrorParams };
        if (body.code && isApiErrorCode(body.code)) {
          code = body.code;
          params = body.params;
          message = formatApiErrorMessage(code, locale, params);
        } else if (Array.isArray(body.message)) {
          message = body.message.join('；');
        } else if (typeof body.message === 'string' && body.message.trim()) {
          message = body.message;
        }
      }
    } else if (exception instanceof Error && exception.message.trim()) {
      message = exception.message;
    }

    response.status(status).json({
      success: false,
      error: message,
      code,
      params: params ?? null,
      data: extraData,
    });
  }
}

export function jsonResponse<T>(data: T, message?: string) {
  return {
    success: true,
    data: serializeBigInt(data),
    message,
  };
}
