import { AppException } from "./app-exception";
import { ExceptionCode } from "./exception-code";
import type { ExceptionResult } from "./exception-result";
import { ApiExceptionHandler } from "./handlers/api-exception-handler";
import { BrowserExceptionHandler } from "./handlers/browser-exception-handler";
import {
  DEFAULT_EXCEPTION_MESSAGE,
  FallbackExceptionHandler,
} from "./handlers/fallback-exception-handler";
import { NetworkExceptionHandler } from "./handlers/network-exception-handler";
import { PaymentExceptionHandler } from "./handlers/payment-exception-handler";
import { StorageExceptionHandler } from "./handlers/storage-exception-handler";
import { ValidationExceptionHandler } from "./handlers/validation-exception-handler";

export class ExceptionHandler {
  static handle(error: unknown): ExceptionResult {
    if (error instanceof AppException) {
      return error.toResult();
    }

    if (ApiExceptionHandler.canHandle(error)) {
      return ApiExceptionHandler.handle(error);
    }

    if (NetworkExceptionHandler.canHandle(error)) {
      return NetworkExceptionHandler.handle(error);
    }

    if (BrowserExceptionHandler.canHandle(error)) {
      return BrowserExceptionHandler.handle(error);
    }

    if (ValidationExceptionHandler.canHandle(error)) {
      return ValidationExceptionHandler.handle(error);
    }

    if (StorageExceptionHandler.canHandle(error)) {
      return StorageExceptionHandler.handle(error);
    }

    if (PaymentExceptionHandler.canHandle(error)) {
      return PaymentExceptionHandler.handle(error);
    }

    return FallbackExceptionHandler.handle(error);
  }

  static message(error: unknown, fallbackMessage?: string): string {
    const result = ExceptionHandler.handle(error);
    if (
      fallbackMessage &&
      result.code === ExceptionCode.unknown &&
      result.message === DEFAULT_EXCEPTION_MESSAGE
    ) {
      return fallbackMessage;
    }
    return result.message;
  }

  static toError(error: unknown): AppException {
    if (error instanceof AppException) return error;
    return new AppException(ExceptionHandler.handle(error), error);
  }
}
