import { BadRequestException } from '@nestjs/common';

export type Result<T, E> = {
  result?: T;
  error?: E;
};

export function success<T, E>(res: T): Result<T, E> {
  return {
    result: res,
  };
}

export function fail<T, E>(error: E): Result<T, E> {
  return {
    error: error,
  };
}

export function getOrThrow<T, E>(res: Result<T, E>): T {
  if (res.result) return res.result;
  throw res.error ? res.error : 'UNKNOWN_ERROR';
}

export async function successOrError<T, E>(
  cb: () => Promise<T>,
): Promise<Result<T, E>> {
  try {
    const res = await cb();
    return {
      result: res,
    };
  } catch (ex) {
    return {
      error: ex,
    };
  }
}

export type AppError = Error & {
  app: true;
};

export function createAppError(msg: string | Error): AppError {
  let err: any;
  if (typeof msg === 'string') {
    err = new Error(msg);
  } else if (typeof msg === 'object') {
    err = msg;
  } else {
    err = new Error('UNKNOWN_APP_ERROR');
  }
  err.name = 'ApplicationError';
  return err;
}

export async function getOrAppErrorOrThrow<T>(
  fn: () => Promise<T>,
  onAppError?: (err: Error) => Error,
  onOtherError?: (err: Error) => Error,
): Promise<T> {
  try {
    return await fn();
  } catch (ex) {
    if (ex && ex.name === 'ApplicationError') {
      throw onAppError ? onAppError(ex) : ex;
    }
    throw onOtherError ? onOtherError(ex) : ex;
  }
}

export function BadRequestErrorHandler(err: Error): Error {
  return new BadRequestException(err.message);
}
