import { extractContinuations, Context, MethodCallback as MethodCallbackOrig } from '@coinweb/contract-kit';
import {
  getContextCall,
  getContextGenesis,
  getContextSystem,
  getContextTx,
  writeToResultFile,
} from '@coinweb/contract-kit/dist/esm/context';
import { getMethodName } from '@coinweb/contract-kit/dist/esm/method';

import { ContractHandlers, MethodCallback } from './types';

/**
 * Adds a method handler for a specific method name.
 * @param contract_module - Contract module containing the method.
 * @param methodName - The name of the method.
 * @param handler - The method callback to add.
 */
export function addMethodHandler(contract_module: ContractHandlers, methodName: string, handler: MethodCallback): void {
  contract_module.handlers[methodName] = handler;
}

/**
 * Retrieves the method handler for a specific method name.
 * @param contract_module - Contract module containing the method.
 * @param methodName - The name of the method.
 * @returns The method callback for the specified method name.
 * @throws Will throw an error if no handler is specified for the method name.
 */
export function getMethodHandler(contract_module: ContractHandlers, methodName: string): MethodCallback {
  const handler = contract_module.handlers[methodName];
  if (!handler) {
    throw Error('Handler not specified for this method name');
  }
  return handler;
}

/**
 * Executes the handler for the method specified in the transaction context.
 * The results (new transactions) are written to the result file.
 * @param contractModule - Contract module containing the method.
 */
export async function executeHandler(contractModule: ContractHandlers): Promise<void> {
  const contextTx = getContextTx();
  const contextCall = getContextCall();
  const genesis = getContextGenesis();
  const system = getContextSystem();
  const context: Context = {
    tx: contextTx,
    call: contextCall,
    genesis,
    system,
    continuations: extractContinuations(contextTx),
  };
  const method = getMethodName(context);
  const handler = getMethodHandler(contractModule, method);

  console.log('EXECUTE >>>', method);
  try {
    const txs = await handler(context);
    writeToResultFile(txs);
  } catch (error) {
    if (error instanceof Error) {
      console.log('ERROR >>>', error.message);
      console.log('ERROR >>>', error.stack);
    } else {
      console.log('ERROR >>>', error);
    }
    throw error;
  }
}
