/**
 * 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,
  getStructDecoder,
  getStructEncoder,
  getU128Decoder,
  getU128Encoder,
  getU32Decoder,
  getU32Encoder,
  getU64Decoder,
  getU64Encoder,
  getU8Decoder,
  getU8Encoder,
  transformEncoder,
  type Account,
  type Address,
  type EncodedAccount,
  type FetchAccountConfig,
  type FetchAccountsConfig,
  type FixedSizeCodec,
  type FixedSizeDecoder,
  type FixedSizeEncoder,
  type MaybeAccount,
  type MaybeEncodedAccount,
  type ReadonlyUint8Array,
} from "@solana/kit";
import {
  getRewardInfoDecoder,
  getRewardInfoEncoder,
  getTokenInfoDecoder,
  getTokenInfoEncoder,
  type RewardInfo,
  type RewardInfoArgs,
  type TokenInfo,
  type TokenInfoArgs,
} from "../types";

export const FARM_STATE_DISCRIMINATOR = new Uint8Array([
  198, 102, 216, 74, 63, 66, 163, 190,
]);

export function getFarmStateDiscriminatorBytes() {
  return fixEncoderSize(getBytesEncoder(), 8).encode(FARM_STATE_DISCRIMINATOR);
}

export type FarmState = {
  discriminator: ReadonlyUint8Array;
  farmAdmin: Address;
  globalConfig: Address;
  token: TokenInfo;
  rewardInfos: Array<RewardInfo>;
  numRewardTokens: bigint;
  /** Data used to calculate the rewards of the user */
  numUsers: bigint;
  /**
   * The number of token in the `farm_vault` staked (getting rewards and fees)
   * Set such as `farm_vault.amount = total_staked_amount + total_pending_amount`
   */
  totalStakedAmount: bigint;
  farmVault: Address;
  farmVaultsAuthority: Address;
  farmVaultsAuthorityBump: bigint;
  /**
   * Only used for delegate farms
   * Set to `default()` otherwise
   */
  delegateAuthority: Address;
  /**
   * Raw representation of a `TimeUnit`
   * Seconds = 0, Slots = 1
   */
  timeUnit: number;
  /**
   * Automatically set to true in case of a full authority withdrawal
   * If true, the farm is frozen and no more deposits are allowed
   */
  isFarmFrozen: number;
  /**
   * Indicates if the farm is a delegate farm
   * If true, the farm is a delegate farm and the `delegate_authority` is set*
   */
  isFarmDelegated: number;
  /** If set to 1, indicates that the "reward user once" feature is enabled */
  isRewardUserOnceEnabled: number;
  isHarvestingPermissionless: number;
  padding0: ReadonlyUint8Array;
  /**
   * Withdraw authority for the farm, allowed to lock deposited funds and withdraw them
   * Set to `default()` if unused (only the depositors can withdraw their funds)
   */
  withdrawAuthority: Address;
  /**
   * Delay between a user deposit and the moment it is considered as staked
   * 0 if unused
   */
  depositWarmupPeriod: number;
  /** Delay between a user unstake and the ability to withdraw his deposit. */
  withdrawalCooldownPeriod: number;
  /** Total active stake of tokens in the farm (scaled from `Decimal` representation). */
  totalActiveStakeScaled: bigint;
  /**
   * Total pending stake of tokens in the farm (scaled from `Decimal` representation).
   * (can be used by `withdraw_authority` but don't get rewards or fees)
   */
  totalPendingStakeScaled: bigint;
  /** Total pending amount of tokens in the farm */
  totalPendingAmount: bigint;
  /** Slashed amounts from early withdrawal */
  slashedAmountCurrent: bigint;
  slashedAmountCumulative: bigint;
  slashedAmountSpillAddress: Address;
  /** Locking stake */
  lockingMode: bigint;
  lockingStartTimestamp: bigint;
  lockingDuration: bigint;
  lockingEarlyWithdrawalPenaltyBps: bigint;
  depositCapAmount: bigint;
  scopePrices: Address;
  scopeOraclePriceId: bigint;
  scopeOracleMaxAge: bigint;
  pendingFarmAdmin: Address;
  strategyId: Address;
  delegatedRpsAdmin: Address;
  vaultId: Address;
  secondDelegatedAuthority: Address;
  padding: Array<bigint>;
};

export type FarmStateArgs = {
  farmAdmin: Address;
  globalConfig: Address;
  token: TokenInfoArgs;
  rewardInfos: Array<RewardInfoArgs>;
  numRewardTokens: number | bigint;
  /** Data used to calculate the rewards of the user */
  numUsers: number | bigint;
  /**
   * The number of token in the `farm_vault` staked (getting rewards and fees)
   * Set such as `farm_vault.amount = total_staked_amount + total_pending_amount`
   */
  totalStakedAmount: number | bigint;
  farmVault: Address;
  farmVaultsAuthority: Address;
  farmVaultsAuthorityBump: number | bigint;
  /**
   * Only used for delegate farms
   * Set to `default()` otherwise
   */
  delegateAuthority: Address;
  /**
   * Raw representation of a `TimeUnit`
   * Seconds = 0, Slots = 1
   */
  timeUnit: number;
  /**
   * Automatically set to true in case of a full authority withdrawal
   * If true, the farm is frozen and no more deposits are allowed
   */
  isFarmFrozen: number;
  /**
   * Indicates if the farm is a delegate farm
   * If true, the farm is a delegate farm and the `delegate_authority` is set*
   */
  isFarmDelegated: number;
  /** If set to 1, indicates that the "reward user once" feature is enabled */
  isRewardUserOnceEnabled: number;
  isHarvestingPermissionless: number;
  padding0: ReadonlyUint8Array;
  /**
   * Withdraw authority for the farm, allowed to lock deposited funds and withdraw them
   * Set to `default()` if unused (only the depositors can withdraw their funds)
   */
  withdrawAuthority: Address;
  /**
   * Delay between a user deposit and the moment it is considered as staked
   * 0 if unused
   */
  depositWarmupPeriod: number;
  /** Delay between a user unstake and the ability to withdraw his deposit. */
  withdrawalCooldownPeriod: number;
  /** Total active stake of tokens in the farm (scaled from `Decimal` representation). */
  totalActiveStakeScaled: number | bigint;
  /**
   * Total pending stake of tokens in the farm (scaled from `Decimal` representation).
   * (can be used by `withdraw_authority` but don't get rewards or fees)
   */
  totalPendingStakeScaled: number | bigint;
  /** Total pending amount of tokens in the farm */
  totalPendingAmount: number | bigint;
  /** Slashed amounts from early withdrawal */
  slashedAmountCurrent: number | bigint;
  slashedAmountCumulative: number | bigint;
  slashedAmountSpillAddress: Address;
  /** Locking stake */
  lockingMode: number | bigint;
  lockingStartTimestamp: number | bigint;
  lockingDuration: number | bigint;
  lockingEarlyWithdrawalPenaltyBps: number | bigint;
  depositCapAmount: number | bigint;
  scopePrices: Address;
  scopeOraclePriceId: number | bigint;
  scopeOracleMaxAge: number | bigint;
  pendingFarmAdmin: Address;
  strategyId: Address;
  delegatedRpsAdmin: Address;
  vaultId: Address;
  secondDelegatedAuthority: Address;
  padding: Array<number | bigint>;
};

/** Gets the encoder for {@link FarmStateArgs} account data. */
export function getFarmStateEncoder(): FixedSizeEncoder<FarmStateArgs> {
  return transformEncoder(
    getStructEncoder([
      ["discriminator", fixEncoderSize(getBytesEncoder(), 8)],
      ["farmAdmin", getAddressEncoder()],
      ["globalConfig", getAddressEncoder()],
      ["token", getTokenInfoEncoder()],
      ["rewardInfos", getArrayEncoder(getRewardInfoEncoder(), { size: 10 })],
      ["numRewardTokens", getU64Encoder()],
      ["numUsers", getU64Encoder()],
      ["totalStakedAmount", getU64Encoder()],
      ["farmVault", getAddressEncoder()],
      ["farmVaultsAuthority", getAddressEncoder()],
      ["farmVaultsAuthorityBump", getU64Encoder()],
      ["delegateAuthority", getAddressEncoder()],
      ["timeUnit", getU8Encoder()],
      ["isFarmFrozen", getU8Encoder()],
      ["isFarmDelegated", getU8Encoder()],
      ["isRewardUserOnceEnabled", getU8Encoder()],
      ["isHarvestingPermissionless", getU8Encoder()],
      ["padding0", fixEncoderSize(getBytesEncoder(), 3)],
      ["withdrawAuthority", getAddressEncoder()],
      ["depositWarmupPeriod", getU32Encoder()],
      ["withdrawalCooldownPeriod", getU32Encoder()],
      ["totalActiveStakeScaled", getU128Encoder()],
      ["totalPendingStakeScaled", getU128Encoder()],
      ["totalPendingAmount", getU64Encoder()],
      ["slashedAmountCurrent", getU64Encoder()],
      ["slashedAmountCumulative", getU64Encoder()],
      ["slashedAmountSpillAddress", getAddressEncoder()],
      ["lockingMode", getU64Encoder()],
      ["lockingStartTimestamp", getU64Encoder()],
      ["lockingDuration", getU64Encoder()],
      ["lockingEarlyWithdrawalPenaltyBps", getU64Encoder()],
      ["depositCapAmount", getU64Encoder()],
      ["scopePrices", getAddressEncoder()],
      ["scopeOraclePriceId", getU64Encoder()],
      ["scopeOracleMaxAge", getU64Encoder()],
      ["pendingFarmAdmin", getAddressEncoder()],
      ["strategyId", getAddressEncoder()],
      ["delegatedRpsAdmin", getAddressEncoder()],
      ["vaultId", getAddressEncoder()],
      ["secondDelegatedAuthority", getAddressEncoder()],
      ["padding", getArrayEncoder(getU64Encoder(), { size: 74 })],
    ]),
    (value) => ({ ...value, discriminator: FARM_STATE_DISCRIMINATOR }),
  );
}

/** Gets the decoder for {@link FarmState} account data. */
export function getFarmStateDecoder(): FixedSizeDecoder<FarmState> {
  return getStructDecoder([
    ["discriminator", fixDecoderSize(getBytesDecoder(), 8)],
    ["farmAdmin", getAddressDecoder()],
    ["globalConfig", getAddressDecoder()],
    ["token", getTokenInfoDecoder()],
    ["rewardInfos", getArrayDecoder(getRewardInfoDecoder(), { size: 10 })],
    ["numRewardTokens", getU64Decoder()],
    ["numUsers", getU64Decoder()],
    ["totalStakedAmount", getU64Decoder()],
    ["farmVault", getAddressDecoder()],
    ["farmVaultsAuthority", getAddressDecoder()],
    ["farmVaultsAuthorityBump", getU64Decoder()],
    ["delegateAuthority", getAddressDecoder()],
    ["timeUnit", getU8Decoder()],
    ["isFarmFrozen", getU8Decoder()],
    ["isFarmDelegated", getU8Decoder()],
    ["isRewardUserOnceEnabled", getU8Decoder()],
    ["isHarvestingPermissionless", getU8Decoder()],
    ["padding0", fixDecoderSize(getBytesDecoder(), 3)],
    ["withdrawAuthority", getAddressDecoder()],
    ["depositWarmupPeriod", getU32Decoder()],
    ["withdrawalCooldownPeriod", getU32Decoder()],
    ["totalActiveStakeScaled", getU128Decoder()],
    ["totalPendingStakeScaled", getU128Decoder()],
    ["totalPendingAmount", getU64Decoder()],
    ["slashedAmountCurrent", getU64Decoder()],
    ["slashedAmountCumulative", getU64Decoder()],
    ["slashedAmountSpillAddress", getAddressDecoder()],
    ["lockingMode", getU64Decoder()],
    ["lockingStartTimestamp", getU64Decoder()],
    ["lockingDuration", getU64Decoder()],
    ["lockingEarlyWithdrawalPenaltyBps", getU64Decoder()],
    ["depositCapAmount", getU64Decoder()],
    ["scopePrices", getAddressDecoder()],
    ["scopeOraclePriceId", getU64Decoder()],
    ["scopeOracleMaxAge", getU64Decoder()],
    ["pendingFarmAdmin", getAddressDecoder()],
    ["strategyId", getAddressDecoder()],
    ["delegatedRpsAdmin", getAddressDecoder()],
    ["vaultId", getAddressDecoder()],
    ["secondDelegatedAuthority", getAddressDecoder()],
    ["padding", getArrayDecoder(getU64Decoder(), { size: 74 })],
  ]);
}

/** Gets the codec for {@link FarmState} account data. */
export function getFarmStateCodec(): FixedSizeCodec<FarmStateArgs, FarmState> {
  return combineCodec(getFarmStateEncoder(), getFarmStateDecoder());
}

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

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

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

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

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

export function getFarmStateSize(): number {
  return 8336;
}
