import { createInitializeCandyMachineInstruction, createSetCollectionInstruction } from '@metaplex-solarti/mpl-candy-machine';
import { Keypair } from '@solarti/web3.js';
import { getCandyMachineV2AccountSizeFromData } from '../helpers.mjs';
import { findCandyMachineV2CollectionPda } from '../pdas.mjs';
import { CandyMachineV2Program } from '../program.mjs';
import { toCandyMachineV2InstructionData } from '../models/CandyMachineV2.mjs';
import { assertSameCurrencies, SOL } from '../../../types/Amount.mjs';
import { useOperation, makeConfirmOptionsFinalizedOnMainnet } from '../../../types/Operation.mjs';
import { toBigNumber } from '../../../types/BigNumber.mjs';
import { toPublicKey } from '../../../types/PublicKey.mjs';
import { TransactionBuilder } from '../../../utils/TransactionBuilder.mjs';
import { isSigner } from '../../../types/Signer.mjs';
import { ExpectedSignerError } from '../../../errors/SdkError.mjs';

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

const Key = 'CreateCandyMachineV2Operation';

/**
 * Creates a brand new Candy Machine.
 *
 * ```ts
 * const { candyMachine } = await metaplex
 *   .candyMachinesV2()
 *   .create({
 *     sellerFeeBasisPoints: 500, // 5% royalties
 *     price: sol(1.3), // 1.3 SOL
 *     itemsAvailable: toBigNumber(1000), // 1000 items available
 *   };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
const createCandyMachineV2Operation = useOperation(Key);

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

/**
 * @group Operations
 * @category Handlers
 */
const createCandyMachineV2OperationHandler = {
  async handle(operation, metaplex, scope) {
    const builder = await createCandyMachineV2Builder(metaplex, operation.input, scope);
    scope.throwIfCanceled();
    const confirmOptions = makeConfirmOptionsFinalizedOnMainnet(metaplex, scope.confirmOptions);
    const output = await builder.sendAndConfirm(metaplex, confirmOptions);
    scope.throwIfCanceled();
    const candyMachine = await metaplex.candyMachinesV2().findByAddress({
      address: output.candyMachineSigner.publicKey
    }, scope);
    return {
      ...output,
      candyMachine
    };
  }
};

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

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

/**
 * Creates a brand new Candy Machine.
 *
 * ```ts
 * const transactionBuilder = await metaplex
 *   .candyMachinesV2()
 *   .builders()
 *   .create({
 *     sellerFeeBasisPoints: 500, // 5% royalties
 *     price: sol(1.3), // 1.3 SOL
 *     itemsAvailable: toBigNumber(1000), // 1000 items available
 *   });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
const createCandyMachineV2Builder = async (metaplex, params, options = {}) => {
  const {
    programs,
    payer = metaplex.rpc().getDefaultFeePayer()
  } = options;
  const candyMachine = params.candyMachine ?? Keypair.generate();
  const authority = params.authority ?? metaplex.identity();
  const collection = params.collection ?? null;
  const {
    data,
    wallet,
    tokenMint
  } = toCandyMachineV2InstructionData(candyMachine.publicKey, {
    ...params,
    wallet: params.wallet ?? metaplex.identity().publicKey,
    tokenMint: params.tokenMint ?? null,
    symbol: params.symbol ?? '',
    maxEditionSupply: params.maxEditionSupply ?? toBigNumber(0),
    isMutable: params.isMutable ?? true,
    retainAuthority: params.retainAuthority ?? true,
    goLiveDate: params.goLiveDate ?? null,
    endSettings: params.endSettings ?? null,
    creators: params.creators ?? [{
      address: metaplex.identity().publicKey,
      share: 100,
      verified: false
    }],
    hiddenSettings: params.hiddenSettings ?? null,
    whitelistMintSettings: params.whitelistMintSettings ?? null,
    gatekeeper: params.gatekeeper ?? null
  });
  const initializeInstruction = createInitializeCandyMachineInstruction({
    candyMachine: candyMachine.publicKey,
    wallet,
    authority: toPublicKey(authority),
    payer: payer.publicKey
  }, {
    data
  });
  if (tokenMint) {
    initializeInstruction.keys.push({
      pubkey: tokenMint,
      isWritable: false,
      isSigner: false
    });
  } else {
    assertSameCurrencies(params.price, SOL);
  }
  return TransactionBuilder.make().setFeePayer(payer).setContext({
    candyMachineSigner: candyMachine,
    wallet,
    authority: toPublicKey(authority),
    creators: data.creators
  })

  // Create an empty account for the candy machine.
  .add(await metaplex.system().builders().createAccount({
    newAccount: candyMachine,
    space: getCandyMachineV2AccountSizeFromData(data),
    program: CandyMachineV2Program.publicKey,
    instructionKey: params.createAccountInstructionKey ?? 'createAccount'
  }, {
    payer,
    programs
  }))

  // Initialize the candy machine account.
  .add({
    instruction: initializeInstruction,
    signers: [candyMachine, payer],
    key: params.initializeCandyMachineInstructionKey ?? 'initializeCandyMachine'
  })

  // Set the collection.
  .when(!!collection, builder => {
    if (!isSigner(authority)) {
      throw new ExpectedSignerError('authority', 'PublicKey', {
        problemSuffix: 'You are trying to create a Candy Machine with a Collection NFT. ' + 'In order for the Collection NFT to be set successfully, you must provide the authority as a Signer.',
        solution: 'Please provide the "authority" parameter as a Signer if you want to set the Collection NFT upon creation. ' + 'Alternatively, you may remove the "collection" parameter to create a Candy Machine without an associated Collection NFT.'
      });
    }
    const collectionMint = collection;
    const metadata = metaplex.nfts().pdas().metadata({
      mint: collectionMint,
      programs
    });
    const edition = metaplex.nfts().pdas().masterEdition({
      mint: collectionMint,
      programs
    });
    const collectionPda = findCandyMachineV2CollectionPda(candyMachine.publicKey);
    const collectionAuthorityRecord = metaplex.nfts().pdas().collectionAuthorityRecord({
      mint: collectionMint,
      collectionAuthority: collectionPda,
      programs
    });
    return builder.add({
      instruction: createSetCollectionInstruction({
        candyMachine: candyMachine.publicKey,
        authority: toPublicKey(authority),
        collectionPda,
        payer: payer.publicKey,
        metadata,
        mint: collectionMint,
        edition,
        collectionAuthorityRecord,
        tokenMetadataProgram: metaplex.programs().getTokenMetadata().address
      }),
      signers: [authority],
      key: params.setCollectionInstructionKey ?? 'setCollection'
    });
  });
};

export { createCandyMachineV2Builder, createCandyMachineV2Operation, createCandyMachineV2OperationHandler };
//# sourceMappingURL=createCandyMachineV2.mjs.map
