import { constructStore } from '@coinweb/contract-kit/dist/esm/operations/store';

import { constructResultClaim } from '../../claims/result';
import { opMarker } from '../../globals/promise';
import { setNextExec, stopExecution } from '../../runtime';
import { getAwaitedTasksCount, pushAwaitedTask } from '../../runtime/awaitedTasks';
import { getUsedOps, startSavingUsedOps, stopSavingUsedOps, shiftResolvedOp } from '../../runtime/resolvedOps';
import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../utils';
import { uuid } from '../../utils';

let isRootDetected = false;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const cwait = <TAsyncCallback extends (...args: any[]) => Promise<unknown>>(asyncCallback: TAsyncCallback) => {
  let isRoot = false;

  return (async (...args: Parameters<TAsyncCallback>) => {
    if (!isRootDetected) {
      isRootDetected = true;
      isRoot = true;
    }

    if (isRoot) {
      return asyncCallback(...args);
    }

    const { op, isOp } = shiftResolvedOp();

    if (!isOp) {
      pushAwaitedTask({
        ExecOp: {
          id: uuid(),
        },
      });

      const result = new Promise(() => null) as Promise<unknown> & { [opMarker]: boolean };

      result[opMarker] = true;

      return result as ReturnType<TAsyncCallback>;
    } else {
      if (isResolvedSlotOp(op)) {
        return new Promise(() => null);
      }

      if (isResolvedExecOp(op)) {
        setNextExec(async () => {
          startSavingUsedOps();
          await asyncCallback(...args);
          stopSavingUsedOps();

          if (!getAwaitedTasksCount()) {
            console.log('push result claim');

            pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps())));
          }
        });

        stopExecution(); //Check: maybe does no affect

        return;
      }

      if (isResolvedChildOp(op)) {
        return asyncCallback(...args);
      }

      throw new Error('Exec or Child operation not found');
    }
  }) as TAsyncCallback;
};
