'use strict';

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

var splToken = require('@solarti/spl-token');
var SdkError = require('../../../errors/SdkError.cjs');
var Operation = require('../../../types/Operation.cjs');
var TransactionBuilder = require('../../../utils/TransactionBuilder.cjs');
var PublicKey = require('../../../types/PublicKey.cjs');
var Signer = require('../../../types/Signer.cjs');

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

const Key = 'CreateTokenOperation';

/**
 * Creates a new token account.
 *
 * ```ts
 * const { token } = await metaplex.tokens().createToken({ mint });
 * ```
 *
 * @group Operations
 * @category Constructors
 */
const createTokenOperation = Operation.useOperation(Key);

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

/**
 * @group Operations
 * @category Handlers
 */
const createTokenOperationHandler = {
  async handle(operation, metaplex, scope) {
    const builder = await createTokenBuilder(metaplex, operation.input, scope);
    scope.throwIfCanceled();
    const confirmOptions = Operation.makeConfirmOptionsFinalizedOnMainnet(metaplex, scope.confirmOptions);
    const output = await builder.sendAndConfirm(metaplex, confirmOptions);
    scope.throwIfCanceled();
    const token = await metaplex.tokens().findTokenByAddress({
      address: output.tokenAddress
    }, scope);
    return {
      ...output,
      token
    };
  }
};

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

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

/**
 * Creates a new token account.
 *
 * ```ts
 * const transactionBuilder = await metaplex.tokens().builders().createToken({ mint });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
const createTokenBuilder = async (metaplex, params, options = {}) => {
  const {
    programs,
    payer = metaplex.rpc().getDefaultFeePayer()
  } = options;
  const {
    mint,
    owner = metaplex.identity().publicKey,
    token
  } = params;
  const tokenProgram = metaplex.programs().getToken(programs);
  const associatedTokenProgram = metaplex.programs().getAssociatedToken(programs);
  const isAssociatedToken = token === undefined;
  const builder = TransactionBuilder.TransactionBuilder.make().setFeePayer(payer);
  if (isAssociatedToken) {
    const associatedTokenAddress = metaplex.tokens().pdas().associatedTokenAccount({
      mint,
      owner,
      programs
    });
    return builder.setContext({
      tokenAddress: associatedTokenAddress
    })

    // Create an associated token account.
    .add({
      instruction: splToken.createAssociatedTokenAccountInstruction(payer.publicKey, associatedTokenAddress, owner, mint, tokenProgram.address, associatedTokenProgram.address),
      signers: [payer],
      key: params.createAssociatedTokenAccountInstructionKey ?? 'createAssociatedTokenAccount'
    });
  }
  return builder.setFeePayer(payer).setContext({
    tokenAddress: token.publicKey
  })

  // Create an empty account for the Token.
  .add(await metaplex.system().builders().createAccount({
    newAccount: token,
    space: splToken.ACCOUNT_SIZE,
    program: tokenProgram.address,
    instructionKey: params.createAccountInstructionKey ?? 'createAccount'
  }, {
    payer,
    programs
  }))

  // Initialize the Token.
  .add({
    instruction: splToken.createInitializeAccountInstruction(token.publicKey, mint, owner, tokenProgram.address),
    signers: [token],
    key: params.initializeTokenInstructionKey ?? 'initializeToken'
  });
};

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

/**
 * @group Transaction Builders
 * @category Constructors
 * @internal
 */
const createTokenIfMissingBuilder = async (metaplex, params, options = {}) => {
  const {
    programs,
    payer = metaplex.rpc().getDefaultFeePayer()
  } = options;
  const {
    mint,
    owner = metaplex.identity().publicKey,
    token,
    tokenExists = true,
    tokenVariable = 'token'
  } = params;
  const destination = token ?? metaplex.tokens().pdas().associatedTokenAccount({
    mint,
    owner,
    programs
  });
  const destinationAddress = PublicKey.toPublicKey(destination);
  const builder = TransactionBuilder.TransactionBuilder.make().setFeePayer(payer).setContext({
    tokenAddress: destinationAddress
  });
  if (tokenExists) {
    return builder;
  }

  // When creating a token account, ensure it is passed as a Signer.
  if (token && !Signer.isSigner(token)) {
    throw new SdkError.ExpectedSignerError(tokenVariable, 'PublicKey', {
      problemSuffix: `The provided "${tokenVariable}" account ` + `at address [${destinationAddress}] does not exist. ` + `Therefore, it needs to be created and passed as a Signer.`,
      solution: `If you want to create the "${tokenVariable}" account, then please pass it as a Signer. ` + `Alternatively, you can pass the owner account as a PublicKey instead to ` + `use (or create) an associated token account.`
    });
  }
  return builder.add(await metaplex.tokens().builders().createToken({
    ...params,
    mint,
    owner,
    token
  }, {
    programs,
    payer
  }));
};

exports.createTokenBuilder = createTokenBuilder;
exports.createTokenIfMissingBuilder = createTokenIfMissingBuilder;
exports.createTokenOperation = createTokenOperation;
exports.createTokenOperationHandler = createTokenOperationHandler;
//# sourceMappingURL=createToken.cjs.map
