import { assertCandyGuardProgram } from '../programs.mjs';
import { toCandyMachine } from '../models/CandyMachine.mjs';
import { assertAccountExists } from '../../../types/Account.mjs';
import { toCandyGuard } from '../models/CandyGuard.mjs';
import { PublicKey } from '@solarti/web3.js';

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

const Key = 'FindCandyMachineByAddressOperation';

/**
 * Find an existing Candy Machine by its address.
 *
 * ```ts
 * const candyMachine = await metaplex
 *   .candyMachines()
 *   .findbyAddress({ address };
 * ```
 *
 * @group Operations
 * @category Constructors
 */
const findCandyMachineByAddressOperation = _findCandyMachineByAddressOperation;
// eslint-disable-next-line @typescript-eslint/naming-convention
function _findCandyMachineByAddressOperation(input) {
  return {
    key: Key,
    input
  };
}
_findCandyMachineByAddressOperation.key = Key;

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

/**
 * @group Operations
 * @category Handlers
 */
const findCandyMachineByAddressOperationHandler = {
  async handle(operation, metaplex, scope) {
    const {
      address
    } = operation.input;
    const {
      commitment,
      programs
    } = scope;
    const potentialCandyGuardAddress = metaplex.candyMachines().pdas().candyGuard({
      base: address,
      programs
    });
    const [candyMachineAccount, potentialCandyGuardAccount] = await metaplex.rpc().getMultipleAccounts([address, potentialCandyGuardAddress], commitment);
    scope.throwIfCanceled();
    assertAccountExists(candyMachineAccount, 'CandyMachine');
    const candyMachine = toCandyMachine(candyMachineAccount);
    const mintAuthority = candyMachine.mintAuthorityAddress;

    // Optimisation that tries to load both the Candy Machine
    // And the Candy Guard in one RPC call assuming the Candy
    // Machine's address is the base address of the Candy Guard.
    if (potentialCandyGuardAccount.exists && potentialCandyGuardAccount.publicKey.equals(mintAuthority)) {
      return {
        ...candyMachine,
        candyGuard: toCandyGuard(potentialCandyGuardAccount, metaplex)
      };
    }

    // If the Candy Machine's mint authority is not a PDA,
    // it cannot have an associated Candy Guard.
    if (PublicKey.isOnCurve(mintAuthority)) {
      return candyMachine;
    }

    // Fetch the content of the mint authority PDA.
    const mintAuthorityAccount = await metaplex.rpc().getAccount(mintAuthority, commitment);
    scope.throwIfCanceled();
    try {
      // Identity the program owner as a Candy Guard program
      // and parse the Candy Guard accordingly.
      assertAccountExists(mintAuthorityAccount);
      const program = metaplex.programs().get(mintAuthorityAccount.owner);
      assertCandyGuardProgram(program);
      return {
        ...candyMachine,
        candyGuard: toCandyGuard(mintAuthorityAccount, metaplex)
      };
    } catch (error) {
      // If anything goes wrong, assume there is no Candy Guard
      // attached to this Candy Machine.
      return candyMachine;
    }
  }
};

export { findCandyMachineByAddressOperation, findCandyMachineByAddressOperationHandler };
//# sourceMappingURL=findCandyMachineByAddress.mjs.map
