import {
  Context,
  extractContractArgs,
  NewTx,
  ResolvedOperation,
  getMethodArguments,
  constructContinueTx,
  constructContractRef,
} from '@coinweb/contract-kit';
import { getCallParameters, queue } from 'lib/onchain';

import { context, getRawContext, setRawContext } from './context';
import { getAwaitedOps } from './promisifedOps/awaited';
import { pushResolvedOp } from './promisifedOps/resolved';

let abortExecution: (() => void) | null = null;

const handleState = () => {
  const ctx = getRawContext();

  const resolvedOps = extractContractArgs(ctx.tx);
  const methodArgs = getMethodArguments(ctx) as [unknown, unknown[], ResolvedOperation[]];

  const initialArgs = methodArgs[1] ?? [];
  const previousOps = methodArgs[2] ?? [];
  const allResolvedOps = [...previousOps, ...resolvedOps];

  pushResolvedOp(allResolvedOps);

  return { args: initialArgs, methodName: methodArgs[0] as string, ops: allResolvedOps };
};

export const executor =
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (method: (...args: any[]) => Promise<void>) =>
    async (ctx: Context): Promise<NewTx[]> => {
      setRawContext(ctx);

      const { args, methodName, ops } = handleState();

      const execution = new Promise<void>((resolve, reject) => {
        abortExecution = resolve;

        method(...args).then(() => resolve(), reject);
      });

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

      await execution;

      const { authInfo, availableCweb } = getCallParameters(ctx);

      const awaitedOps = getAwaitedOps();

      if (!awaitedOps.length) {
        return queue.gateway.unlock(ctx);
      }

      const txFee = 700n + BigInt(awaitedOps.length) * 100n;

      return [
        constructContinueTx(
          ctx,
          [],
          [
            {
              callInfo: {
                ref: constructContractRef(context.issuer, []),
                methodInfo: {
                  methodName,
                  methodArgs: [args, ops],
                },
                contractInfo: {
                  providedCweb: availableCweb - txFee,
                  authenticated: authInfo,
                },
                contractArgs: awaitedOps,
              },
            },
          ]
        ),
      ];
    };
