'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var mplTokenMetadata = require('@metaplex-solarti/mpl-token-metadata');
var isEqual = require('lodash.isequal');
var SdkError = require('../../../errors/SdkError.cjs');
var Operation = require('../../../types/Operation.cjs');
var TransactionBuilder = require('../../../utils/TransactionBuilder.cjs');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var isEqual__default = /*#__PURE__*/_interopDefaultLegacy(isEqual);

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

const Key = 'UpdateNftOperation';

/**
 * Updates an existing NFT or SFT.
 *
 * ```ts
 * await metaplex
 *   .nfts()
 *   .update({ nftOrSft, name: "My new NFT name" };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
const updateNftOperation = Operation.useOperation(Key);

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

/**
 * @group Operations
 * @category Handlers
 */
const updateNftOperationHandler = {
  handle: async (operation, metaplex, scope) => {
    const builder = updateNftBuilder(metaplex, operation.input, scope);
    if (builder.isEmpty()) {
      throw new SdkError.NoInstructionsToSendError(Key);
    }
    return builder.sendAndConfirm(metaplex, scope.confirmOptions);
  }
};

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

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

/**
 * Updates an existing NFT or SFT.
 *
 * ```ts
 * const transactionBuilder = metaplex
 *   .nfts()
 *   .builders()
 *   .update({ nftOrSft, name: "My new NFT name" });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
const updateNftBuilder = (metaplex, params, options = {}) => {
  const {
    programs,
    payer = metaplex.rpc().getDefaultFeePayer()
  } = options;
  const {
    nftOrSft,
    updateAuthority = metaplex.identity()
  } = params;

  // Programs.
  const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
  const updateInstructionDataWithoutChanges = toInstructionData(nftOrSft);
  const updateInstructionData = toInstructionData(nftOrSft, params);
  const shouldSendUpdateInstruction = !isEqual__default["default"](updateInstructionData, updateInstructionDataWithoutChanges);
  const isRemovingVerifiedCollection = !!nftOrSft.collection && !!nftOrSft.collection.verified && params.collection === null;
  const isOverridingVerifiedCollection = !!nftOrSft.collection && !!nftOrSft.collection.verified && !!params.collection && !params.collection.equals(nftOrSft.collection.address);
  const shouldUnverifyCurrentCollection = isRemovingVerifiedCollection || isOverridingVerifiedCollection;
  const creatorsInput = params.creators ?? nftOrSft.creators;
  const verifyAdditionalCreatorInstructions = creatorsInput.filter(creator => {
    const currentCreator = nftOrSft.creators.find(({
      address
    }) => address.equals(creator.address));
    const currentlyVerified = currentCreator?.verified ?? false;
    return !!creator.authority && !currentlyVerified;
  }).map(creator => {
    return metaplex.nfts().builders().verifyCreator({
      mintAddress: nftOrSft.address,
      creator: creator.authority
    }, {
      payer,
      programs
    });
  });
  return TransactionBuilder.TransactionBuilder.make()

  // Unverify current collection before overriding it.
  // Otherwise, the previous collection size will not be properly decremented.
  .when(shouldUnverifyCurrentCollection, builder => builder.add(metaplex.nfts().builders().unverifyCollection({
    mintAddress: nftOrSft.address,
    collectionMintAddress: nftOrSft.collection?.address,
    collectionAuthority: updateAuthority,
    isSizedCollection: params.oldCollectionIsSized ?? true
  }, {
    programs,
    payer
  })))

  // Update the metadata account.
  .when(shouldSendUpdateInstruction, builder => builder.add({
    instruction: mplTokenMetadata.createUpdateMetadataAccountV2Instruction({
      metadata: metaplex.nfts().pdas().metadata({
        mint: nftOrSft.address,
        programs
      }),
      updateAuthority: updateAuthority.publicKey
    }, {
      updateMetadataAccountArgsV2: updateInstructionData
    }, tokenMetadataProgram.address),
    signers: [updateAuthority],
    key: params.updateMetadataInstructionKey ?? 'updateMetadata'
  }))

  // Verify additional creators.
  .add(...verifyAdditionalCreatorInstructions)

  // Verify collection.
  .when(!!params.collection && !!params.collectionAuthority, builder => builder.add(metaplex.nfts().builders().verifyCollection({
    mintAddress: nftOrSft.address,
    collectionMintAddress: params.collection,
    collectionAuthority: params.collectionAuthority,
    isDelegated: params.collectionAuthorityIsDelegated ?? false,
    isSizedCollection: params.collectionIsSized ?? true
  }, {
    programs,
    payer
  })));
};
const toInstructionData = (nftOrSft, input = {}) => {
  const creators = input.creators === undefined ? nftOrSft.creators : input.creators.map(creator => {
    const currentCreator = nftOrSft.creators.find(({
      address
    }) => address.equals(creator.address));
    return {
      ...creator,
      verified: currentCreator?.verified ?? false
    };
  });
  const currentCollection = nftOrSft.collection ? {
    ...nftOrSft.collection,
    key: nftOrSft.collection.address
  } : null;
  const newCollection = input.collection ? {
    key: input.collection,
    verified: false
  } : null;
  return {
    updateAuthority: input.newUpdateAuthority ?? null,
    primarySaleHappened: input.primarySaleHappened ?? null,
    isMutable: input.isMutable ?? null,
    data: {
      name: input.name ?? nftOrSft.name,
      symbol: input.symbol ?? nftOrSft.symbol,
      uri: input.uri ?? nftOrSft.uri,
      sellerFeeBasisPoints: input.sellerFeeBasisPoints ?? nftOrSft.sellerFeeBasisPoints,
      creators: creators.length > 0 ? creators : null,
      uses: input.uses === undefined ? nftOrSft.uses : input.uses,
      collection: input.collection === undefined ? currentCollection : newCollection
    }
  };
};

exports.updateNftBuilder = updateNftBuilder;
exports.updateNftOperation = updateNftOperation;
exports.updateNftOperationHandler = updateNftOperationHandler;
//# sourceMappingURL=updateNft.cjs.map
