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

import { constructResultClaim } from '../claims/result';
import { context } from '../context';
import { stopExecution } from '../executor';
import { opMarker } from '../global';
import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../utils';
import { uuid } from '../utils';

import { getAwaitedTasks, pushAwaitedTask } from './awaited';
import { getUsedOps, saveUsedOps, shiftResolvedOp } from './resolved';

let isRootDetected = false;

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

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

    console.log('isRoot:', isRoot);
    console.log('isChild:', context.isChild);

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

    console.log('child logic');

    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)) {
        console.log('cwait-slotOp');
        return new Promise(() => null);
      }

      if (isResolvedExecOp(op)) {
        console.log('cwait-execOp');

        saveUsedOps();

        const result = await asyncCallback(...args);

        const awaitedOps = getAwaitedTasks();

        if (!awaitedOps.length) {
          pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps())));

          stopExecution();
        }

        return result;
      }

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

      console.log('cwait-error');
      throw new Error('Read operation not found');
    }
  }) as TAsyncCallback;
};
