import { constructContractRef, ContractIssuer, CwebStore, GStore, GTake } from '@coinweb/contract-kit';
import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take';

import { PreparedExtendedStoreOp } from '../../../types';
import {
  constructMutexExecOpsBlock,
  constructMutexExecOpsClaimRead,
  constructMutexLockClaimRangeRead,
} from '../claims';
import { execOpsMethodName } from '../methods';
import { MutexExecOpArgs } from '../types';

export const constructExecOpsCall = ({
  issuer,
  ops,
  processId,
  execId,
}: {
  issuer: ContractIssuer;
  ops: (GTake<CwebTake> | PreparedExtendedStoreOp)[];
  processId: string;
  execId?: string;
}) => {
  const opsFee = BigInt(ops.length) * 100n;
  const execCallInnerFee = 700n;

  const takeOpsAdditionalFee = BigInt(ops.filter((op) => 'TakeOp' in op).length) * 100n;
  const prepareTakeOpsInnerFee = takeOpsAdditionalFee > 0n ? 700n : 0n;

  const storedCweb = ops
    .filter((op) => 'StoreOp' in op)
    .reduce((total, op) => {
      if (op.providedCweb) {
        return total + op.providedCweb;
      }
      return total + BigInt(op.StoreOp.fees_stored);
    }, 0n);

  const callFee = 700n;

  const providedCweb = opsFee + execCallInnerFee + takeOpsAdditionalFee + prepareTakeOpsInnerFee + storedCweb + callFee;
  const contractArgsFee = 100n;

  const preparedOps: (GTake<CwebTake> | GStore<CwebStore>)[] = ops.map((op) => {
    if ('StoreOp' in op) {
      const preparedStoreOp = { ...op };
      delete preparedStoreOp.providedCweb;
      return preparedStoreOp;
    }

    return op;
  });

  return {
    callInfo: {
      ref: constructContractRef(issuer, []),
      methodInfo: {
        methodName: execOpsMethodName,
        methodArgs: [preparedOps, processId, execId] satisfies MutexExecOpArgs,
      },
      contractInfo: {
        providedCweb,
        authenticated: null,
      },
      contractArgs: [constructMutexLockClaimRangeRead(issuer)],
    },
    fee: providedCweb + contractArgsFee,
    ops: execId
      ? ([constructMutexExecOpsBlock(execId, issuer), constructMutexExecOpsClaimRead(issuer, execId)] as const)
      : [],
  };
};
