import {
  constructClaim,
  constructContinueTx,
  constructContractRef,
  constructStore,
  constructTake,
  Context,
  extractContractArgs,
  extractRead,
  passCwebFrom,
} from '@coinweb/contract-kit';
import { getCallParameters, getContractIssuer } from 'lib/onchain';
import { toHex, TypedClaim } from 'lib/shared';

import { lockFee } from '../settings';
import { MutexLockState, MutexWaitNotifyArgs } from '../types';
import { isMatchLockKeys } from '../utils';

import { waitMethodName } from './names';

export const mutexExecLock = (context: Context) => {
  const { availableCweb } = getCallParameters(context);
  const issuer = getContractIssuer(context);

  const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map(
    ({ content }) => content as TypedClaim<MutexLockState>
  );

  if (!lockQueue) {
    throw new Error('No lock queue found');
  }

  const alreadyLockedKeys = lockQueue.filter(({ body }) => body.locked).map(({ key }) => key);

  const lockCandidate = lockQueue.find(
    ({ body, key }) => !body.locked && !alreadyLockedKeys.some((lockedKey) => isMatchLockKeys(lockedKey, key))
  );

  if (!lockCandidate) {
    return [];
  }

  return [
    constructContinueTx(
      context,
      [
        passCwebFrom(issuer, availableCweb),
        constructTake(lockCandidate.key),
        constructStore(
          constructClaim(
            lockCandidate.key,
            { ...lockCandidate.body, locked: true },
            toHex(BigInt(lockCandidate.fees_stored) - lockFee)
          )
        ),
      ],
      [
        {
          callInfo: {
            ref: constructContractRef(issuer, []),
            methodInfo: {
              methodName: waitMethodName,
              methodArgs: [
                0,
                {
                  timestamp: (lockCandidate.key.second_part as [number, string])[0],
                  lockId: (lockCandidate.key.second_part as [number, string])[1],
                },
              ] satisfies MutexWaitNotifyArgs,
            },
            contractInfo: {
              providedCweb: availableCweb + BigInt(lockCandidate.fees_stored) - 1000n,
              authenticated: null,
            },
            contractArgs: [],
          },
        },
      ]
    ),
  ];
};
