/**
 * This code was AUTOGENERATED using the codama library.
 * Please DO NOT EDIT THIS FILE, instead use visitors
 * to add features, then rerun codama to update it.
 *
 * @see https://github.com/codama-idl/codama
 */

import {
  assertAccountExists,
  assertAccountsExist,
  combineCodec,
  decodeAccount,
  fetchEncodedAccount,
  fetchEncodedAccounts,
  fixDecoderSize,
  fixEncoderSize,
  getAddressDecoder,
  getAddressEncoder,
  getArrayDecoder,
  getArrayEncoder,
  getBytesDecoder,
  getBytesEncoder,
  getOptionDecoder,
  getOptionEncoder,
  getStructDecoder,
  getStructEncoder,
  getU8Decoder,
  getU8Encoder,
  transformEncoder,
  type Account,
  type Address,
  type Codec,
  type Decoder,
  type EncodedAccount,
  type Encoder,
  type FetchAccountConfig,
  type FetchAccountsConfig,
  type MaybeAccount,
  type MaybeEncodedAccount,
  type Option,
  type OptionOrNullable,
  type ReadonlyUint8Array,
} from '@solana/kit';
import {
  getCpiRuleDecoder,
  getCpiRuleEncoder,
  getMetadataAdditionalFieldRuleDecoder,
  getMetadataAdditionalFieldRuleEncoder,
  getTransferAmountRuleDecoder,
  getTransferAmountRuleEncoder,
  type CpiRule,
  type CpiRuleArgs,
  type MetadataAdditionalFieldRule,
  type MetadataAdditionalFieldRuleArgs,
  type TransferAmountRule,
  type TransferAmountRuleArgs,
} from '../types';

export const GUARD_V1_DISCRIMINATOR = new Uint8Array([
  185, 149, 156, 78, 245, 108, 172, 68,
]);

export function getGuardV1DiscriminatorBytes() {
  return fixEncoderSize(getBytesEncoder(), 8).encode(GUARD_V1_DISCRIMINATOR);
}

export type GuardV1 = {
  discriminator: ReadonlyUint8Array;
  /** Mint token representing the guard, do not confuse with the mint of the token being transferred. */
  mint: Address;
  /** Bump seed for the guard account. */
  bump: number;
  /** CPI ruleset for the guard. */
  cpiRule: Option<CpiRule>;
  /** Transfer amount ruleset for the guard. */
  transferAmountRule: Option<TransferAmountRule>;
  /** Additional fields ruleset for the guard. */
  additionalFieldsRule: Array<MetadataAdditionalFieldRule>;
};

export type GuardV1Args = {
  /** Mint token representing the guard, do not confuse with the mint of the token being transferred. */
  mint: Address;
  /** Bump seed for the guard account. */
  bump: number;
  /** CPI ruleset for the guard. */
  cpiRule: OptionOrNullable<CpiRuleArgs>;
  /** Transfer amount ruleset for the guard. */
  transferAmountRule: OptionOrNullable<TransferAmountRuleArgs>;
  /** Additional fields ruleset for the guard. */
  additionalFieldsRule: Array<MetadataAdditionalFieldRuleArgs>;
};

export function getGuardV1Encoder(): Encoder<GuardV1Args> {
  return transformEncoder(
    getStructEncoder([
      ['discriminator', fixEncoderSize(getBytesEncoder(), 8)],
      ['mint', getAddressEncoder()],
      ['bump', getU8Encoder()],
      ['cpiRule', getOptionEncoder(getCpiRuleEncoder())],
      ['transferAmountRule', getOptionEncoder(getTransferAmountRuleEncoder())],
      [
        'additionalFieldsRule',
        getArrayEncoder(getMetadataAdditionalFieldRuleEncoder()),
      ],
    ]),
    (value) => ({ ...value, discriminator: GUARD_V1_DISCRIMINATOR })
  );
}

export function getGuardV1Decoder(): Decoder<GuardV1> {
  return getStructDecoder([
    ['discriminator', fixDecoderSize(getBytesDecoder(), 8)],
    ['mint', getAddressDecoder()],
    ['bump', getU8Decoder()],
    ['cpiRule', getOptionDecoder(getCpiRuleDecoder())],
    ['transferAmountRule', getOptionDecoder(getTransferAmountRuleDecoder())],
    [
      'additionalFieldsRule',
      getArrayDecoder(getMetadataAdditionalFieldRuleDecoder()),
    ],
  ]);
}

export function getGuardV1Codec(): Codec<GuardV1Args, GuardV1> {
  return combineCodec(getGuardV1Encoder(), getGuardV1Decoder());
}

export function decodeGuardV1<TAddress extends string = string>(
  encodedAccount: EncodedAccount<TAddress>
): Account<GuardV1, TAddress>;
export function decodeGuardV1<TAddress extends string = string>(
  encodedAccount: MaybeEncodedAccount<TAddress>
): MaybeAccount<GuardV1, TAddress>;
export function decodeGuardV1<TAddress extends string = string>(
  encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
): Account<GuardV1, TAddress> | MaybeAccount<GuardV1, TAddress> {
  return decodeAccount(
    encodedAccount as MaybeEncodedAccount<TAddress>,
    getGuardV1Decoder()
  );
}

export async function fetchGuardV1<TAddress extends string = string>(
  rpc: Parameters<typeof fetchEncodedAccount>[0],
  address: Address<TAddress>,
  config?: FetchAccountConfig
): Promise<Account<GuardV1, TAddress>> {
  const maybeAccount = await fetchMaybeGuardV1(rpc, address, config);
  assertAccountExists(maybeAccount);
  return maybeAccount;
}

export async function fetchMaybeGuardV1<TAddress extends string = string>(
  rpc: Parameters<typeof fetchEncodedAccount>[0],
  address: Address<TAddress>,
  config?: FetchAccountConfig
): Promise<MaybeAccount<GuardV1, TAddress>> {
  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
  return decodeGuardV1(maybeAccount);
}

export async function fetchAllGuardV1(
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
  addresses: Array<Address>,
  config?: FetchAccountsConfig
): Promise<Account<GuardV1>[]> {
  const maybeAccounts = await fetchAllMaybeGuardV1(rpc, addresses, config);
  assertAccountsExist(maybeAccounts);
  return maybeAccounts;
}

export async function fetchAllMaybeGuardV1(
  rpc: Parameters<typeof fetchEncodedAccounts>[0],
  addresses: Array<Address>,
  config?: FetchAccountsConfig
): Promise<MaybeAccount<GuardV1>[]> {
  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
  return maybeAccounts.map((maybeAccount) => decodeGuardV1(maybeAccount));
}
