/* eslint-disable @typescript-eslint/no-explicit-any */
import { Context, NewTx, getMethodArguments, isSelfCall } from '@coinweb/contract-kit';

import { Task } from '../types';

import { context, getRawContext, handleContext } from './context';
import { getAwaitedTasks } from './promisifiedFeatures';
import { pushResolvedOp } from './promisifiedFeatures/runtime/resolvedOps';
import { constructTx } from './utils';

let abortExecution: ((result: { isFullyExecuted: boolean; awaitedTasks: Task[] }) => void) | null = null;

export const executor = (method: (...args: any[]) => Promise<void>) => {
  return async (ctx: Context): Promise<NewTx[]> => {
    console.log('executor-start >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
    const shouldRestart = handleContext(ctx);
    pushResolvedOp(context.ops);

    if (getMethodArguments(getRawContext()).length > 2 && !isSelfCall(ctx)) {
      throw new Error('Wrong contract call, check the call arguments');
    }

    if (shouldRestart) {
      const awaitedTasks = getAwaitedTasks();

      return constructTx(awaitedTasks, false);
    }

    const execution = new Promise<{ isFullyExecuted: boolean; awaitedTasks: Task[] }>((resolve, reject) => {
      abortExecution = resolve;

      method(...context.initialArgs).then(
        () => {
          console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-resolved');
          const awaitedTasks = getAwaitedTasks();
          resolve({ isFullyExecuted: true, awaitedTasks });
        },
        (error) => {
          console.log(' <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<executor-rejected');
          console.log(error);
          reject(error);
        }
      );
    });

    //@ts-expect-error
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call
    os.setTimeout(() => {
      const awaitedTasks = getAwaitedTasks();
      abortExecution?.({ isFullyExecuted: false, awaitedTasks });
    }, 0);

    try {
      const { isFullyExecuted, awaitedTasks } = await execution;

      return constructTx(awaitedTasks, isFullyExecuted);
    } catch (error) {
      console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-error');
      console.log((error as Error).message);
      throw error;
    }
  };
};

export const stopExecution = () => {
  console.log('stopExecution');
  const awaitedTasks = getAwaitedTasks();
  abortExecution?.({ isFullyExecuted: false, awaitedTasks });
};
