import { IntegerType, PublicKey, intToBigInt, utf8ToBytes } from '@stacks/common';
import { NetworkClientParam, StacksNetwork } from '@stacks/network';
import {
  ClarityType,
  ClarityValue,
  NonFungiblePostCondition,
  PostCondition,
  ResponseErrorCV,
  StacksTransactionWire,
  StxPostCondition,
  UnsignedContractCallOptions,
  bufferCV,
  bufferCVFromString,
  cvToString,
  fetchCallReadOnlyFunction,
  getAddressFromPrivateKey,
  getCVTypeString,
  hash160,
  makeRandomPrivKey,
  makeUnsignedContractCall,
  noneCV,
  publicKeyToAddress,
  someCV,
  standardPrincipalCV,
  tupleCV,
  uintCV,
} from '@stacks/transactions';
import { decodeFQN, getZonefileHash } from './utils';

export const BNS_CONTRACT_NAME = 'bns';

export interface PriceFunction {
  base: IntegerType;
  coefficient: IntegerType;
  b1: IntegerType;
  b2: IntegerType;
  b3: IntegerType;
  b4: IntegerType;
  b5: IntegerType;
  b6: IntegerType;
  b7: IntegerType;
  b8: IntegerType;
  b9: IntegerType;
  b10: IntegerType;
  b11: IntegerType;
  b12: IntegerType;
  b13: IntegerType;
  b14: IntegerType;
  b15: IntegerType;
  b16: IntegerType;
  nonAlphaDiscount: IntegerType;
  noVowelDiscount: IntegerType;
}

export interface BnsContractCallOptions {
  functionName: string;
  functionArgs: ClarityValue[];
  publicKey: PublicKey;
  network: StacksNetwork;
  postConditions?: PostCondition[];
}

async function makeBnsContractCall(
  options: BnsContractCallOptions
): Promise<StacksTransactionWire> {
  const txOptions: UnsignedContractCallOptions = {
    contractAddress: options.network.bootAddress,
    contractName: BNS_CONTRACT_NAME,
    functionName: options.functionName,
    functionArgs: options.functionArgs,
    publicKey: options.publicKey,
    validateWithAbi: false,
    network: options.network,
    postConditions: options.postConditions,
  };

  return makeUnsignedContractCall(txOptions);
}

export interface BnsReadOnlyOptions {
  functionName: string;
  functionArgs: ClarityValue[];
  senderAddress: string;
  network: StacksNetwork;
}

async function callReadOnlyBnsFunction(
  options: BnsReadOnlyOptions & NetworkClientParam
): Promise<ClarityValue> {
  return fetchCallReadOnlyFunction({
    ...options,
    contractAddress: options.network.bootAddress,
    contractName: BNS_CONTRACT_NAME,
  });
}

/**
 * Can register name options
 *
 * @param  {String} fullyQualifiedName - the fully qualified name ("name.namespace") to check
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface CanRegisterNameOptions {
  fullyQualifiedName: string;
  network: StacksNetwork;
}

/**
 * Check if name can be registered
 *
 * @param {string} fullyQualifiedName - the fully qualified name to check
 * @param {StacksNetwork} network - the Stacks network to broadcast transaction to
 *
 * @returns {Promise} that resolves to true if the operation succeeds
 */
export async function canRegisterName({
  fullyQualifiedName,
  network,
}: CanRegisterNameOptions): Promise<boolean> {
  const bnsFunctionName = 'can-name-be-registered';
  const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot register a subdomain using registerName');
  }

  // Create a random address as input to read-only function call
  // Not used by BNS contract function but required by core node API
  // https://github.com/blockstack/stacks-blockchain/blob/master/src/net/http.rs#L1796
  const randomPrivateKey = makeRandomPrivKey();
  const randomAddress = getAddressFromPrivateKey(randomPrivateKey);

  return callReadOnlyBnsFunction({
    functionName: bnsFunctionName,
    senderAddress: randomAddress,
    functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
    network,
  }).then((responseCV: ClarityValue) => {
    if (responseCV.type === ClarityType.ResponseOk) {
      return responseCV.value.type === ClarityType.BoolTrue;
    } else {
      return false;
    }
  });
}

/**
 * Get namespace price options
 *
 * @param  {String} namespace - the namespace to get the price of
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface GetNamespacePriceOptions {
  namespace: string;
  network: StacksNetwork;
}

/**
 * Get price of namespace registration in microstacks
 *
 * @param {string} namespace - the namespace
 * @param {StacksNetwork} network - the Stacks network to use
 *
 * @returns {Promise} that resolves to a BN object number of microstacks if the operation succeeds
 */
export async function getNamespacePrice({
  namespace,
  network,
}: GetNamespacePriceOptions): Promise<bigint> {
  const bnsFunctionName = 'get-namespace-price';

  // Create a random address as input to read-only function call
  // Not used by BNS contract function but required by core node API
  // https://github.com/blockstack/stacks-blockchain/blob/master/src/net/http.rs#L1796
  const randomPrivateKey = makeRandomPrivKey();
  const randomAddress = getAddressFromPrivateKey(randomPrivateKey);

  return callReadOnlyBnsFunction({
    functionName: bnsFunctionName,
    senderAddress: randomAddress,
    functionArgs: [bufferCVFromString(namespace)],
    network,
  }).then((responseCV: ClarityValue) => {
    if (responseCV.type === ClarityType.ResponseOk) {
      if (responseCV.value.type === ClarityType.Int || responseCV.value.type === ClarityType.UInt) {
        return BigInt(responseCV.value.value);
      } else {
        throw new Error('Response did not contain a number');
      }
    } else if (responseCV.type === ClarityType.ResponseErr) {
      throw new Error(cvToString(responseCV.value));
    } else {
      throw new Error(`Unexpected Clarity Value type: ${getCVTypeString(responseCV)}`);
    }
  });
}

/**
 * Get name price options
 *
 * @param  {String} fullyQualifiedName - the fully qualified name ("name.namespace") to get the price of
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface GetNamePriceOptions {
  fullyQualifiedName: string;
  network: StacksNetwork;
}

/**
 * Get price of name registration in microstacks
 *
 * @param {string} fullyQualifiedName - the fully qualified name
 * @param {StacksNetwork} network - the Stacks network to use
 *
 * @returns {Promise} that resolves to a BN object number of microstacks if the operation succeeds
 */
export async function getNamePrice({
  fullyQualifiedName,
  network,
}: GetNamePriceOptions): Promise<bigint> {
  const bnsFunctionName = 'get-name-price';
  const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot get subdomain name price');
  }

  // Create a random address as input to read-only function call
  // Not used by BNS contract function but required by core node API
  // https://github.com/blockstack/stacks-blockchain/blob/master/src/net/http.rs#L1796
  const randomPrivateKey = makeRandomPrivKey();
  const randomAddress = getAddressFromPrivateKey(randomPrivateKey);

  return callReadOnlyBnsFunction({
    functionName: bnsFunctionName,
    senderAddress: randomAddress,
    functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
    network,
  }).then((responseCV: ClarityValue) => {
    if (responseCV.type === ClarityType.ResponseOk) {
      if (responseCV.value.type === ClarityType.Int || responseCV.value.type === ClarityType.UInt) {
        return BigInt(responseCV.value.value);
      } else {
        throw new Error('Response did not contain a number');
      }
    } else {
      const errorResponse = responseCV as ResponseErrorCV;
      throw new Error(cvToString(errorResponse.value));
    }
  });
}

/**
 * Preorder namespace options
 */
export interface PreorderNamespaceOptions {
  /** the namespace to preorder */
  namespace: string;
  /** salt used to generate the preorder namespace hash */
  salt: string;
  /** amount of STX to burn for the registration */
  stxToBurn: IntegerType;
  /** the private key to sign the transaction */
  publicKey: string;
  /** the Stacks blockchain network to use */
  network: StacksNetwork;
}

/**
 * Generates a namespace preorder transaction.
 * First step in registering a namespace. This transaction does not reveal the namespace that is
 * about to be registered. And it sets the amount of STX to be burned for the registration.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {PreorderNamespaceOptions} options - an options object for the preorder
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildPreorderNamespaceTx({
  namespace,
  salt,
  stxToBurn,
  publicKey,
  network,
}: PreorderNamespaceOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'namespace-preorder';
  const saltedNamespaceBytes = utf8ToBytes(`${namespace}${salt}`);
  const hashedSaltedNamespace = hash160(saltedNamespaceBytes);

  const burnSTXPostCondition: StxPostCondition = {
    type: 'stx-postcondition',
    address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
    condition: 'eq',
    amount: intToBigInt(stxToBurn),
  };

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [bufferCV(hashedSaltedNamespace), uintCV(stxToBurn)],
    publicKey,
    network,
    postConditions: [burnSTXPostCondition],
  });
}

/**
 * Reveal namespace options
 */
export interface RevealNamespaceOptions {
  /** the namespace to reveal */
  namespace: string;
  /** salt used to generate the preorder namespace hash */
  salt: string;
  /** an object containing the price function for the namespace */
  priceFunction: PriceFunction;
  /** the number of blocks name registrations are valid for in the namespace */
  lifetime: IntegerType;
  /** the STX address used for name imports */
  namespaceImportAddress: string;
  /** the key to sign the transaction */
  publicKey: string;
  /** the Stacks blockchain network to use */
  network: StacksNetwork;
}

/**
 * Generates a namespace reveal transaction.
 * Second step in registering a namespace.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {RevealNamespaceOptions} options - an options object for the reveal
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildRevealNamespaceTx({
  namespace,
  salt,
  priceFunction,
  lifetime,
  namespaceImportAddress,
  publicKey,
  network,
}: RevealNamespaceOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'namespace-reveal';

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [
      bufferCVFromString(namespace),
      bufferCVFromString(salt),
      uintCV(priceFunction.base),
      uintCV(priceFunction.coefficient),
      uintCV(priceFunction.b1),
      uintCV(priceFunction.b2),
      uintCV(priceFunction.b3),
      uintCV(priceFunction.b4),
      uintCV(priceFunction.b5),
      uintCV(priceFunction.b6),
      uintCV(priceFunction.b7),
      uintCV(priceFunction.b8),
      uintCV(priceFunction.b9),
      uintCV(priceFunction.b10),
      uintCV(priceFunction.b11),
      uintCV(priceFunction.b12),
      uintCV(priceFunction.b13),
      uintCV(priceFunction.b14),
      uintCV(priceFunction.b15),
      uintCV(priceFunction.b16),
      uintCV(priceFunction.nonAlphaDiscount),
      uintCV(priceFunction.noVowelDiscount),
      uintCV(lifetime),
      standardPrincipalCV(namespaceImportAddress),
    ],
    publicKey,
    network,
  });
}

/**
 * Namespace name import options
 *
 * @param  {String} namespace - the namespace to import name into
 * @param  {String} name - the name to import
 * @param  {String} beneficiary - the address to register the name to
 * @param  {String} zonefileHash - the zonefile hash to register
 * @param  {String} publicKey - the private key to sign the transaction
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface ImportNameOptions {
  namespace: string;
  name: string;
  beneficiary: string;
  zonefile: string;
  publicKey: string;
  network: StacksNetwork;
}

/**
 * Generates a namespace name import transaction.
 * An optional step in namespace registration.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {ImportNameOptions} options - an options object for the name import
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildImportNameTx({
  namespace,
  name,
  beneficiary,
  zonefile,
  publicKey,
  network,
}: ImportNameOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'name-import';
  const zonefileHash = getZonefileHash(zonefile);

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [
      bufferCVFromString(namespace),
      bufferCVFromString(name),
      standardPrincipalCV(beneficiary),
      bufferCV(zonefileHash),
    ],
    publicKey,
    network,
  });
}

/**
 * Ready namespace options
 *
 * @param  {String} namespace - the namespace to ready
 * @param  {String} publicKey - the private key to sign the transaction
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface ReadyNamespaceOptions {
  namespace: string;
  publicKey: string;
  network: StacksNetwork;
}

/**
 * Generates a ready namespace transaction.
 * Final step in namespace registration. This completes the namespace registration and
 * makes the namespace available for name registrations.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {ReadyNamespaceOptions} options - an options object for the namespace ready transaction
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildReadyNamespaceTx({
  namespace,
  publicKey,
  network,
}: ReadyNamespaceOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'namespace-ready';

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [bufferCVFromString(namespace)],
    publicKey,
    network,
  });
}

/**
 * Preorder name options
 */
export interface PreorderNameOptions {
  /** the fully qualified name to preorder including the namespace (myName.id) */
  fullyQualifiedName: string;
  /** salt used to generate the preorder name hash */
  salt: string;
  /** amount of STX to burn for the registration */
  stxToBurn: IntegerType;
  /** the private key to sign the transaction */
  publicKey: PublicKey;
  /** the Stacks blockchain network to use */
  network: StacksNetwork;
}

/**
 * Generates a name preorder transaction.
 * First step in registering a name. This transaction does not reveal the name that is
 * about to be registered. And it sets the amount of STX to be burned for the registration.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {PreorderNameOptions} options - an options object for the preorder
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildPreorderNameTx({
  fullyQualifiedName,
  salt,
  stxToBurn,
  publicKey,
  network,
}: PreorderNameOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'name-preorder';
  const { subdomain } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot preorder a subdomain using preorderName()');
  }
  const saltedNamesBytes = utf8ToBytes(`${fullyQualifiedName}${salt}`);
  const hashedSaltedName = hash160(saltedNamesBytes);

  const burnSTXPostCondition: StxPostCondition = {
    type: 'stx-postcondition',
    address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
    condition: 'eq',
    amount: intToBigInt(stxToBurn),
  };

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [bufferCV(hashedSaltedName), uintCV(stxToBurn)],
    publicKey,
    network,
    postConditions: [burnSTXPostCondition],
  });
}

/**
 * Register name options
 *
 * @param  {String} fullyQualifiedName - the fully qualified name to preorder including the
 *                                        namespace (myName.id)
 * @param  {String} salt - salt used to generate the preorder name hash
 * @param  {String} zonefile - the zonefile to register with the name
 * @param  {String} publicKey - the private key to sign the transaction
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface RegisterNameOptions {
  fullyQualifiedName: string;
  salt: string;
  zonefile: string;
  publicKey: PublicKey;
  network: StacksNetwork;
}

/**
 * Generates a name registration transaction.
 * Second and final step in registering a name.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {RegisterNameOptions} options - an options object for the registration
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildRegisterNameTx({
  fullyQualifiedName,
  salt,
  zonefile,
  publicKey,
  network,
}: RegisterNameOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'name-register';
  const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot register a subdomain using registerName()');
  }

  const zonefileHash = getZonefileHash(zonefile);

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [
      bufferCVFromString(namespace),
      bufferCVFromString(name),
      bufferCVFromString(salt),
      bufferCV(zonefileHash),
    ],
    network,
    publicKey,
  });
}

/**
 * Update name options
 *
 * @param  {String} fullyQualifiedName - the fully qualified name to update including the
 *                                        namespace (myName.id)
 * @param  {String} zonefile - the zonefile to register with the name
 * @param  {String} publicKey - the private key to sign the transaction
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface UpdateNameOptions {
  fullyQualifiedName: string;
  zonefile: string;
  publicKey: string;
  network: StacksNetwork;
}

/**
 * Generates a name update transaction.
 * This changes the zonefile for the registered name.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {UpdateNameOptions} options - an options object for the update
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildUpdateNameTx({
  fullyQualifiedName,
  zonefile,
  publicKey,
  network,
}: UpdateNameOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'name-update';
  const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot update a subdomain using updateName()');
  }
  const zonefileHash = getZonefileHash(zonefile);

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name), bufferCV(zonefileHash)],
    publicKey,
    network,
  });
}

/**
 * Transfer name options
 *
 * @param  {String} fullyQualifiedName - the fully qualified name to transfer including the
 *                                        namespace (myName.id)
 * @param  {String} newOwnerAddress - the recipient address of the name transfer
 * @param  {String} zonefile - the optional zonefile to register with the name
 * @param  {String} publicKey - the private key to sign the transaction
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface TransferNameOptions {
  fullyQualifiedName: string;
  newOwnerAddress: string;
  publicKey: string;
  network: StacksNetwork;
  zonefile?: string;
}

/**
 * Generates a name transfer transaction.
 * This changes the owner of the registered name.
 *
 * Since the underlying NFT will be transferred,
 * you will be required to add a post-condition to this
 * transaction before broadcasting it.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {TransferNameOptions} options - an options object for the transfer
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildTransferNameTx({
  fullyQualifiedName,
  newOwnerAddress,
  zonefile,
  publicKey,
  network,
}: TransferNameOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'name-transfer';
  const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot transfer a subdomain using transferName()');
  }

  const functionArgs = [
    bufferCVFromString(namespace),
    bufferCVFromString(name),
    standardPrincipalCV(newOwnerAddress),
    zonefile ? someCV(bufferCV(getZonefileHash(zonefile))) : noneCV(),
  ];
  const postConditionSender: NonFungiblePostCondition = {
    type: 'nft-postcondition',
    address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
    condition: 'sent',
    asset: `${network.bootAddress}.bns::names`,
    assetId: tupleCV({
      name: bufferCVFromString(name),
      namespace: bufferCVFromString(namespace),
    }),
  };
  const postConditionReceiver: NonFungiblePostCondition = {
    type: 'nft-postcondition',
    address: newOwnerAddress,
    condition: 'not-sent',
    asset: `${network.bootAddress}.bns::names`,
    assetId: tupleCV({
      name: bufferCVFromString(name),
      namespace: bufferCVFromString(namespace),
    }),
  };

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs,
    publicKey,
    network,
    postConditions: [postConditionSender, postConditionReceiver],
  });
}

/**
 * Revoke name options
 *
 * @param  {String} fullyQualifiedName - the fully qualified name to revoke including the
 *                                        namespace (myName.id)
 * @param  {String} publicKey - the private key to sign the transaction
 * @param  {StacksNetwork} network - the Stacks blockchain network to use
 */
export interface RevokeNameOptions {
  fullyQualifiedName: string;
  publicKey: string;
  network: StacksNetwork;
}

/**
 * Generates a name revoke transaction.
 * This revokes a name registration.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {RevokeNameOptions} options - an options object for the revoke
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildRevokeNameTx({
  fullyQualifiedName,
  publicKey,
  network,
}: RevokeNameOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'name-revoke';
  const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot revoke a subdomain using revokeName()');
  }

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
    publicKey,
    network,
  });
}

/**
 * Renew name options
 */
export interface RenewNameOptions {
  /** the fully qualified name to renew including the namespace (myName.id) */
  fullyQualifiedName: string;
  /** amount of STX to burn for the registration */
  stxToBurn: IntegerType;
  /** the private key to sign the transaction */
  publicKey: string;
  /** the Stacks blockchain network to use */
  network: StacksNetwork;
  /** optionally choose a new owner address */
  newOwnerAddress?: string;
  /** optionally update the zonefile hash */
  zonefile?: string;
}

/**
 * Generates a name renew transaction.
 * This renews a name registration.
 *
 * Resolves to the generated StacksTransaction
 *
 * @param  {RenewNameOptions} options - an options object for the renew
 *
 * @return {Promise<StacksTransactionWire>}
 */
export async function buildRenewNameTx({
  fullyQualifiedName,
  stxToBurn,
  newOwnerAddress,
  zonefile,
  publicKey,
  network,
}: RenewNameOptions): Promise<StacksTransactionWire> {
  const bnsFunctionName = 'name-renewal';
  const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
  if (subdomain) {
    throw new Error('Cannot renew a subdomain using renewName()');
  }

  const functionArgs = [
    bufferCVFromString(namespace),
    bufferCVFromString(name),
    uintCV(stxToBurn),
    newOwnerAddress ? someCV(standardPrincipalCV(newOwnerAddress)) : noneCV(),
    zonefile ? someCV(bufferCV(getZonefileHash(zonefile))) : noneCV(),
  ];
  const burnSTXPostCondition: StxPostCondition = {
    type: 'stx-postcondition',
    address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
    condition: 'eq',
    amount: intToBigInt(stxToBurn),
  };

  return makeBnsContractCall({
    functionName: bnsFunctionName,
    functionArgs,
    publicKey,
    network,
    postConditions: [burnSTXPostCondition],
  });
}
