import { createUtilizeInstruction } from '@metaplex-solarti/mpl-token-metadata';
import { useOperation } from '../../../types/Operation.mjs';
import { isSigner } from '../../../types/Signer.mjs';
import { ExpectedSignerError } from '../../../errors/SdkError.mjs';
import { toPublicKey } from '../../../types/PublicKey.mjs';
import { TransactionBuilder } from '../../../utils/TransactionBuilder.mjs';

// -----------------
// Operation
// -----------------

const Key = 'UseNftOperation';

/**
 * Utilizes a usable NFT.
 *
 * ```ts
 * await metaplex.nfts().use({ mintAddress });
 * await metaplex.nfts().use({ mintAddress, numberOfUses: 3 });
 * ```
 *
 * @group Operations
 * @category Constructors
 */
const useNftOperation = useOperation(Key);

/**
 * @group Operations
 * @category Types
 */

/**
 * @group Operations
 * @category Handlers
 */
const useNftOperationHandler = {
  handle: async (operation, metaplex, scope) => {
    return useNftBuilder(metaplex, operation.input, scope).sendAndConfirm(metaplex, scope.confirmOptions);
  }
};

// -----------------
// Builder
// -----------------

/**
 * @group Transaction Builders
 * @category Inputs
 */

/**
 * Utilizes a usable NFT.
 *
 * ```ts
 * const transactionBuilder = metaplex
 *   .nfts()
 *   .builders()
 *   .use({ mintAddress });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
const useNftBuilder = (metaplex, params, options = {}) => {
  const {
    programs,
    payer = metaplex.rpc().getDefaultFeePayer()
  } = options;
  const {
    mintAddress,
    numberOfUses = 1,
    owner = metaplex.identity(),
    useAuthority
  } = params;

  // Programs.
  const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
  if (!isSigner(owner) && !useAuthority) {
    throw new ExpectedSignerError('owner', 'PublicKey', {
      problemSuffix: 'In order to use an NFT you must either provide the owner as a Signer ' + 'or a delegated use authority as a Signer.'
    });
  }

  // PDAs.
  const metadata = metaplex.nfts().pdas().metadata({
    mint: mintAddress,
    programs
  });
  const tokenAccount = params.ownerTokenAccount ?? metaplex.tokens().pdas().associatedTokenAccount({
    mint: mintAddress,
    owner: toPublicKey(owner),
    programs
  });
  const useAuthorityRecord = useAuthority ? metaplex.nfts().pdas().useAuthorityRecord({
    mint: mintAddress,
    useAuthority: useAuthority.publicKey,
    programs
  }) : undefined;
  const programAsBurner = metaplex.nfts().pdas().burner({
    programs
  });
  return TransactionBuilder.make().setFeePayer(payer)

  // Update the metadata account.
  .add({
    instruction: createUtilizeInstruction({
      metadata,
      tokenAccount,
      useAuthority: useAuthority ? useAuthority.publicKey : toPublicKey(owner),
      mint: mintAddress,
      owner: toPublicKey(owner),
      useAuthorityRecord,
      burner: useAuthorityRecord ? programAsBurner : undefined
    }, {
      utilizeArgs: {
        numberOfUses
      }
    }, tokenMetadataProgram.address),
    signers: [owner, useAuthority].filter(isSigner),
    key: params.instructionKey ?? 'utilizeNft'
  });
};

export { useNftBuilder, useNftOperation, useNftOperationHandler };
//# sourceMappingURL=useNft.mjs.map
