import { createCreateMasterEditionV3Instruction } from '@metaplex-solarti/mpl-token-metadata';
import { Keypair } from '@solarti/web3.js';
import { assertNftWithToken } from '../models/Nft.mjs';
import { useOperation, makeConfirmOptionsFinalizedOnMainnet } from '../../../types/Operation.mjs';
import { toPublicKey } from '../../../types/PublicKey.mjs';
import { token } from '../../../types/Amount.mjs';
import { TransactionBuilder } from '../../../utils/TransactionBuilder.mjs';

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

const Key = 'CreateNftOperation';

/**
 * Creates a new NFT.
 *
 * ```ts
 * const { nft } = await metaplex
 *   .nfts()
 *   .create({
 *     name: 'My NFT',
 *     uri: 'https://example.com/my-nft',
 *     sellerFeeBasisPoints: 250, // 2.5%
 *   };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
const createNftOperation = useOperation(Key);

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

/**
 * @group Operations
 * @category Handlers
 */
const createNftOperationHandler = {
  handle: async (operation, metaplex, scope) => {
    const {
      useNewMint = Keypair.generate(),
      useExistingMint,
      tokenOwner = metaplex.identity().publicKey,
      tokenAddress: tokenSigner
    } = operation.input;
    const mintAddress = useExistingMint ?? useNewMint.publicKey;
    const tokenAddress = tokenSigner ? toPublicKey(tokenSigner) : metaplex.tokens().pdas().associatedTokenAccount({
      mint: mintAddress,
      owner: tokenOwner,
      programs: scope.programs
    });
    const tokenAccount = await metaplex.rpc().getAccount(tokenAddress);
    const tokenExists = tokenAccount.exists;
    const builder = await createNftBuilder(metaplex, {
      ...operation.input,
      useNewMint,
      tokenOwner,
      tokenExists
    }, scope);
    scope.throwIfCanceled();
    const confirmOptions = makeConfirmOptionsFinalizedOnMainnet(metaplex, scope.confirmOptions);
    const output = await builder.sendAndConfirm(metaplex, confirmOptions);
    scope.throwIfCanceled();
    const nft = await metaplex.nfts().findByMint({
      mintAddress: output.mintAddress,
      tokenAddress: output.tokenAddress
    }, scope);
    scope.throwIfCanceled();
    assertNftWithToken(nft);
    return {
      ...output,
      nft
    };
  }
};

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

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

/**
 * Creates a new NFT.
 *
 * ```ts
 * const transactionBuilder = await metaplex
 *   .nfts()
 *   .builders()
 *   .create({
 *     name: 'My NFT',
 *     uri: 'https://example.com/my-nft',
 *     sellerFeeBasisPoints: 250, // 2.5%
 *   });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
const createNftBuilder = async (metaplex, params, options = {}) => {
  const {
    programs,
    payer = metaplex.rpc().getDefaultFeePayer()
  } = options;
  const {
    useNewMint = Keypair.generate(),
    updateAuthority = metaplex.identity(),
    mintAuthority = metaplex.identity(),
    tokenOwner = metaplex.identity().publicKey
  } = params;
  const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
  const sftBuilder = await metaplex.nfts().builders().createSft({
    ...params,
    updateAuthority,
    mintAuthority,
    freezeAuthority: mintAuthority.publicKey,
    useNewMint,
    tokenOwner,
    tokenAmount: token(1),
    decimals: 0
  }, {
    programs,
    payer
  });
  const {
    mintAddress,
    metadataAddress,
    tokenAddress
  } = sftBuilder.getContext();
  const masterEditionAddress = metaplex.nfts().pdas().masterEdition({
    mint: mintAddress,
    programs
  });
  return TransactionBuilder.make().setFeePayer(payer).setContext({
    mintAddress,
    metadataAddress,
    masterEditionAddress,
    tokenAddress: tokenAddress
  })

  // Create the mint, the token and the metadata.
  .add(sftBuilder)

  // Create master edition account (prevents further minting).
  .add({
    instruction: createCreateMasterEditionV3Instruction({
      edition: masterEditionAddress,
      mint: mintAddress,
      updateAuthority: updateAuthority.publicKey,
      mintAuthority: mintAuthority.publicKey,
      payer: payer.publicKey,
      metadata: metadataAddress
    }, {
      createMasterEditionArgs: {
        maxSupply: params.maxSupply === undefined ? 0 : params.maxSupply
      }
    }, tokenMetadataProgram.address),
    signers: [payer, mintAuthority, updateAuthority],
    key: params.createMasterEditionInstructionKey ?? 'createMasterEdition'
  });
};

export { createNftBuilder, createNftOperation, createNftOperationHandler };
//# sourceMappingURL=createNft.mjs.map
