import { getTime } from 'lib/onchain';

import { opMarker } from '../../globals/promise';
import { LockedKey } from '../../mutex';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
import { isResolvedLockOp, isResolvedSlotOp, isResolvedUnlockOp, uuid } from '../../utils';

const unlock = (lockId: string, timestamp: number) => {
  console.log('unlockOp');
  let opMarkerValue = false;

  const result = new Promise<void>((resolve, reject) => {
    try {
      const { op, isOp } = shiftResolvedOp();

      if (!isOp) {
        pushAwaitedTask({ UnlockOp: { lockId, timestamp } });
        opMarkerValue = true;
      } else {
        if (isResolvedSlotOp(op)) {
          console.log('unlockOp-slotOp');
          return;
        }

        if (!isResolvedUnlockOp(op)) {
          console.log('unlockOp-error');
          throw new Error('Unlock operation not found');
        }

        resolve();
      }
    } catch (error) {
      reject(error);
    }
  });

  (result as Promise<void> & { [opMarker]: boolean })[opMarker] = opMarkerValue;

  return result;
};

export const lock = (keys: LockedKey[] | LockedKey) => {
  console.log('lockOp');
  let opMarkerValue = false;

  const result = new Promise<() => Promise<void>>((resolve, reject) => {
    try {
      const { op, isOp } = shiftResolvedOp();

      if (!isOp) {
        pushAwaitedTask({
          LockOp: { lockId: uuid(), keys: Array.isArray(keys) ? keys : [keys], timestamp: getTime() },
        });
        opMarkerValue = true;
      } else {
        if (isResolvedSlotOp(op)) {
          console.log('lockOp-slotOp');
          return;
        }

        if (!isResolvedLockOp(op)) {
          console.log('lockOp-error');
          throw new Error('Lock operation not found');
        }

        const result = op.LockOp;

        resolve(() => unlock(result.lockId, result.timestamp));
      }
    } catch (error) {
      reject(error);
    }
  });

  (result as Promise<() => Promise<void>> & { [opMarker]: boolean })[opMarker] = opMarkerValue;

  return result;
};
