import { MutableRefObject, useCallback, useRef, useState } from 'react';
import { ErrorMonitor, QueryResultStatus, queryStatus } from '@wix/bex-core';
import once from 'lodash/once';

export interface PromiseState<T> {
  status: QueryResultStatus<T, unknown>;
  fetchOnce: () => Promise<QueryResultStatus<T, unknown>>;
  promiseFnRef: MutableRefObject<(fn: () => Promise<T>) => void>;
}

export function usePromiseState<T>(
  _promiseFn: () => Promise<T>,
  {
    errorMonitor,
    onError,
  }: {
    errorMonitor: ErrorMonitor;
    onError?: (params: { err: unknown; isOnline: boolean }) => unknown;
  },
): PromiseState<T> {
  const [status, setStatus] = useState<QueryResultStatus<T, unknown>>(() => ({
    ...queryStatus.idle,
    error: null,
    isFetching: false,
  }));

  const promiseFnRef = useRef<() => Promise<T>>(_promiseFn);

  const fetchOnce = useCallback(
    once(async () => {
      let newStatus: QueryResultStatus<T, unknown>;
      try {
        setStatus({
          ...queryStatus.loading,
          error: null,
          isFetching: true,
        });
        const data = await promiseFnRef.current();
        newStatus = {
          ...queryStatus.success,
          error: null,
          isFetching: false,
          data,
        };
        setStatus(newStatus);
      } catch (err) {
        onError?.({
          err,
          isOnline: true,
        });
        errorMonitor.captureException(err);
        newStatus = {
          ...queryStatus.error,
          error: err,
          isFetching: false,
        };
        setStatus(newStatus);
      }

      return newStatus;
    }),
    [],
  );

  return {
    fetchOnce,
    status,
    promiseFnRef,
  };
}
