var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { createSetAuthorityInstruction, createSetCollectionInstruction, createSetMintAuthorityInstruction, createUpdateInstruction as createUpdateCandyMachineInstruction, } from '@metaplex-foundation/mpl-candy-machine-core';
import { isCandyMachine, toCandyMachineData, } from '../models';
import { MissingInputDataError, NoInstructionsToSendError } from '../../../errors';
import { toPublicKey, } from '../../../types';
import { assertObjectHasDefinedKeys, removeUndefinedAttributes, TransactionBuilder, } from '../../../utils';
// -----------------
// Operation
// -----------------
const Key = 'UpdateCandyMachineOperation';
/**
 * Updates the every aspect of an existing Candy Machine, including its
 * authorities, collection and guards (when associated with a Candy Guard).
 *
 * ```ts
 * await metaplex
 *   .candyMachines()
 *   .update({
 *     candyMachine,
 *     sellerFeeBasisPoints: 500,
 *   };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
export const updateCandyMachineOperation = _updateCandyMachineOperation;
// eslint-disable-next-line @typescript-eslint/naming-convention
function _updateCandyMachineOperation(input) {
    return { key: Key, input };
}
_updateCandyMachineOperation.key = Key;
/**
 * @group Operations
 * @category Handlers
 */
export const updateCandyMachineOperationHandler = {
    handle(operation, metaplex, scope) {
        return __awaiter(this, void 0, void 0, function* () {
            const builder = updateCandyMachineBuilder(metaplex, operation.input, scope);
            if (builder.isEmpty()) {
                throw new NoInstructionsToSendError(Key);
            }
            return builder.sendAndConfirm(metaplex, scope.confirmOptions);
        });
    },
};
/**
 * Updates the every aspect of an existing Candy Machine, including its
 * authorities, collection and guards (when associated with a Candy Guard).
 *
 * ```ts
 * const transactionBuilder = await metaplex
 *   .candyMachines()
 *   .builders()
 *   .update({
 *     candyMachine,
 *     sellerFeeBasisPoints: 500,
 *   });
 * ```
 *
 * @group Transaction Builders
 * @category Constructors
 */
export const updateCandyMachineBuilder = (metaplex, params, options = {}) => {
    const { programs, payer = metaplex.rpc().getDefaultFeePayer() } = options;
    const { authority = metaplex.identity(), candyGuardAuthority = authority } = params;
    return (TransactionBuilder.make()
        .setFeePayer(payer)
        // Update Candy Machine data.
        .add(updateCandyMachineDataBuilder(metaplex, params, authority, programs))
        // Update Candy Machine collection.
        .add(updateCandyMachineCollectionBuilder(metaplex, params, authority, payer, programs))
        // Update Candy Guard's guards and groups, if any.
        .add(updateCandyGuardsBuilder(metaplex, params, candyGuardAuthority, payer, programs))
        // Update Candy Machine mint authority.
        .add(updateCandyMachineMintAuthorityBuilder(metaplex, params, authority, programs))
        // Update Candy Machine authority.
        .add(updateCandyMachineAuthorityBuilder(metaplex, params, authority, programs)));
};
const updateCandyMachineDataBuilder = (metaplex, params, authority, programs) => {
    var _a;
    const dataToUpdate = removeUndefinedAttributes({
        itemsAvailable: params.itemsAvailable,
        symbol: params.symbol,
        sellerFeeBasisPoints: params.sellerFeeBasisPoints,
        maxEditionSupply: params.maxEditionSupply,
        isMutable: params.isMutable,
        creators: params.creators,
        itemSettings: params.itemSettings,
    });
    const candyMachineProgram = metaplex.programs().getCandyMachine(programs);
    let data;
    if (Object.keys(dataToUpdate).length === 0) {
        return TransactionBuilder.make();
    }
    else if (isCandyMachine(params.candyMachine)) {
        data = toCandyMachineData(Object.assign(Object.assign({}, params.candyMachine), dataToUpdate));
    }
    else {
        assertObjectHasDefinedKeys(dataToUpdate, [
            'itemsAvailable',
            'symbol',
            'sellerFeeBasisPoints',
            'maxEditionSupply',
            'isMutable',
            'creators',
            'itemSettings',
        ], onMissingInputError);
        data = toCandyMachineData(dataToUpdate);
    }
    return TransactionBuilder.make().add({
        instruction: createUpdateCandyMachineInstruction({
            candyMachine: toPublicKey(params.candyMachine),
            authority: authority.publicKey,
        }, { data }, candyMachineProgram.address),
        signers: [authority],
        key: (_a = params.updateDataInstructionKey) !== null && _a !== void 0 ? _a : 'updateCandyMachineData',
    });
};
const updateCandyMachineCollectionBuilder = (metaplex, params, authority, payer, programs) => {
    var _a, _b;
    if (!params.collection) {
        return TransactionBuilder.make();
    }
    const currentCollectionAddress = (_a = params.collection.currentCollectionAddress) !== null && _a !== void 0 ? _a : (isCandyMachine(params.candyMachine)
        ? params.candyMachine.collectionMintAddress
        : null);
    if (!currentCollectionAddress) {
        throw onMissingInputError(['collection.currentCollectionAddress']);
    }
    // Programs.
    const tokenMetadataProgram = metaplex.programs().getTokenMetadata(programs);
    const candyMachineProgram = metaplex.programs().getCandyMachine(programs);
    // Addresses.
    const candyMachineAddress = toPublicKey(params.candyMachine);
    const collectionAddress = params.collection.address;
    const collectionUpdateAuthority = params.collection.updateAuthority;
    // PDAs.
    const authorityPda = metaplex.candyMachines().pdas().authority({
        candyMachine: candyMachineAddress,
        programs,
    });
    const currentCollectionMetadata = metaplex.nfts().pdas().metadata({
        mint: currentCollectionAddress,
    });
    const currentCollectionAuthorityRecord = metaplex
        .nfts()
        .pdas()
        .collectionAuthorityRecord({
        mint: currentCollectionAddress,
        collectionAuthority: authorityPda,
    });
    const collectionMetadata = metaplex.nfts().pdas().metadata({
        mint: collectionAddress,
    });
    const collectionMasterEdition = metaplex.nfts().pdas().masterEdition({
        mint: collectionAddress,
    });
    const collectionAuthorityRecord = metaplex
        .nfts()
        .pdas()
        .collectionAuthorityRecord({
        mint: collectionAddress,
        collectionAuthority: authorityPda,
    });
    return TransactionBuilder.make().add({
        instruction: createSetCollectionInstruction({
            candyMachine: candyMachineAddress,
            authority: authority.publicKey,
            authorityPda,
            payer: payer.publicKey,
            collectionMint: currentCollectionAddress,
            collectionMetadata: currentCollectionMetadata,
            collectionAuthorityRecord: currentCollectionAuthorityRecord,
            newCollectionUpdateAuthority: collectionUpdateAuthority.publicKey,
            newCollectionMetadata: collectionMetadata,
            newCollectionMint: collectionAddress,
            newCollectionMasterEdition: collectionMasterEdition,
            newCollectionAuthorityRecord: collectionAuthorityRecord,
            tokenMetadataProgram: tokenMetadataProgram.address,
        }, candyMachineProgram.address),
        signers: [authority, payer, collectionUpdateAuthority],
        key: (_b = params.setCollectionInstructionKey) !== null && _b !== void 0 ? _b : 'setCandyMachineCollection',
    });
};
const updateCandyGuardsBuilder = (metaplex, params, candyGuardAuthority, payer, programs) => {
    var _a;
    const guardsToUpdate = removeUndefinedAttributes({
        candyGuard: params.candyGuard,
        guards: params.guards,
        groups: params.groups,
    });
    let args;
    if (Object.keys(guardsToUpdate).length === 0) {
        return TransactionBuilder.make();
    }
    if (isCandyMachine(params.candyMachine) &&
        params.candyMachine.candyGuard) {
        args = Object.assign({ candyGuard: params.candyMachine.candyGuard.address, guards: params.candyMachine.candyGuard.guards, groups: params.candyMachine.candyGuard.groups }, guardsToUpdate);
    }
    else {
        assertObjectHasDefinedKeys(guardsToUpdate, ['candyGuard', 'guards', 'groups'], onMissingInputError);
        args = guardsToUpdate;
    }
    return metaplex
        .candyMachines()
        .builders()
        .updateCandyGuard({
        candyGuard: args.candyGuard,
        guards: args.guards,
        groups: args.groups,
        authority: candyGuardAuthority,
        updateInstructionKey: (_a = params.updateCandyGuardInstructionKey) !== null && _a !== void 0 ? _a : 'updateCandyGuard',
    }, { payer, programs });
};
const updateCandyMachineMintAuthorityBuilder = (metaplex, params, authority, programs) => {
    var _a;
    if (!params.newMintAuthority) {
        return TransactionBuilder.make();
    }
    const candyMachineProgram = metaplex.programs().getCandyMachine(programs);
    return TransactionBuilder.make().add({
        instruction: createSetMintAuthorityInstruction({
            candyMachine: toPublicKey(params.candyMachine),
            authority: authority.publicKey,
            mintAuthority: params.newMintAuthority.publicKey,
        }, candyMachineProgram.address),
        signers: [authority, params.newMintAuthority],
        key: (_a = params.setAuthorityInstructionKey) !== null && _a !== void 0 ? _a : 'setCandyMachineAuthority',
    });
};
const updateCandyMachineAuthorityBuilder = (metaplex, params, authority, programs) => {
    var _a;
    if (!params.newAuthority) {
        return TransactionBuilder.make();
    }
    const candyMachineProgram = metaplex.programs().getCandyMachine(programs);
    return TransactionBuilder.make().add({
        instruction: createSetAuthorityInstruction({
            candyMachine: toPublicKey(params.candyMachine),
            authority: authority.publicKey,
        }, { newAuthority: params.newAuthority }, candyMachineProgram.address),
        signers: [authority],
        key: (_a = params.setAuthorityInstructionKey) !== null && _a !== void 0 ? _a : 'setCandyMachineAuthority',
    });
};
const onMissingInputError = (missingKeys) => new MissingInputDataError(missingKeys, {
    problem: 'When passing the Candy Machine as a `PublicKey` instead of a Candy Machine model ' +
        'the SDK cannot rely on current data to fill the gaps within the provided input.',
    solutionSuffix: ' Alternatively, you can pass the Candy Machine model instead.',
});
//# sourceMappingURL=updateCandyMachine.js.map