/**
 * 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,
  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";

export const USER_STATE_DISCRIMINATOR = new Uint8Array([
  72, 177, 85, 249, 76, 167, 186, 126,
]);

export function getUserStateDiscriminatorBytes() {
  return fixEncoderSize(getBytesEncoder(), 8).encode(USER_STATE_DISCRIMINATOR);
}

export type UserState = {
  discriminator: ReadonlyUint8Array;
  userId: bigint;
  farmState: Address;
  owner: Address;
  /** Indicate if this user state is part of a delegated farm */
  isFarmDelegated: number;
  padding0: ReadonlyUint8Array;
  /**
   * Rewards tally used for computation of gained rewards
   * (scaled from `Decimal` representation).
   */
  rewardsTallyScaled: Array<bigint>;
  /** Number of reward tokens ready for claim */
  rewardsIssuedUnclaimed: Array<bigint>;
  lastClaimTs: Array<bigint>;
  /**
   * User stake deposited and usable, generating rewards and fees.
   * (scaled from `Decimal` representation).
   */
  activeStakeScaled: bigint;
  /**
   * User stake deposited but not usable and not generating rewards yet.
   * (scaled from `Decimal` representation).
   */
  pendingDepositStakeScaled: bigint;
  /**
   * After this timestamp, pending user stake can be moved to user stake
   * Initialized to now() + delayed user stake period
   */
  pendingDepositStakeTs: bigint;
  /**
   * User deposits unstaked, pending for withdrawal, not usable and not generating rewards.
   * (scaled from `Decimal` representation).
   */
  pendingWithdrawalUnstakeScaled: bigint;
  /** After this timestamp, user can withdraw their deposit. */
  pendingWithdrawalUnstakeTs: bigint;
  /** User bump used for account address validation */
  bump: bigint;
  /** Delegatee used for initialisation - useful to check against */
  delegatee: Address;
  lastStakeTs: bigint;
  /**
   * Cumulative rewards issued to the user - ONLY used for stats/analytics
   * DO NOT USE IN ANY CALCULATIONS
   * Old userStates will have this field populated only from the point of release
   * not reflecting any historical data before this was released
   */
  rewardsIssuedCumulative: Array<bigint>;
  padding1: Array<bigint>;
};

export type UserStateArgs = {
  userId: number | bigint;
  farmState: Address;
  owner: Address;
  /** Indicate if this user state is part of a delegated farm */
  isFarmDelegated: number;
  padding0: ReadonlyUint8Array;
  /**
   * Rewards tally used for computation of gained rewards
   * (scaled from `Decimal` representation).
   */
  rewardsTallyScaled: Array<number | bigint>;
  /** Number of reward tokens ready for claim */
  rewardsIssuedUnclaimed: Array<number | bigint>;
  lastClaimTs: Array<number | bigint>;
  /**
   * User stake deposited and usable, generating rewards and fees.
   * (scaled from `Decimal` representation).
   */
  activeStakeScaled: number | bigint;
  /**
   * User stake deposited but not usable and not generating rewards yet.
   * (scaled from `Decimal` representation).
   */
  pendingDepositStakeScaled: number | bigint;
  /**
   * After this timestamp, pending user stake can be moved to user stake
   * Initialized to now() + delayed user stake period
   */
  pendingDepositStakeTs: number | bigint;
  /**
   * User deposits unstaked, pending for withdrawal, not usable and not generating rewards.
   * (scaled from `Decimal` representation).
   */
  pendingWithdrawalUnstakeScaled: number | bigint;
  /** After this timestamp, user can withdraw their deposit. */
  pendingWithdrawalUnstakeTs: number | bigint;
  /** User bump used for account address validation */
  bump: number | bigint;
  /** Delegatee used for initialisation - useful to check against */
  delegatee: Address;
  lastStakeTs: number | bigint;
  /**
   * Cumulative rewards issued to the user - ONLY used for stats/analytics
   * DO NOT USE IN ANY CALCULATIONS
   * Old userStates will have this field populated only from the point of release
   * not reflecting any historical data before this was released
   */
  rewardsIssuedCumulative: Array<number | bigint>;
  padding1: Array<number | bigint>;
};

/** Gets the encoder for {@link UserStateArgs} account data. */
export function getUserStateEncoder(): FixedSizeEncoder<UserStateArgs> {
  return transformEncoder(
    getStructEncoder([
      ["discriminator", fixEncoderSize(getBytesEncoder(), 8)],
      ["userId", getU64Encoder()],
      ["farmState", getAddressEncoder()],
      ["owner", getAddressEncoder()],
      ["isFarmDelegated", getU8Encoder()],
      ["padding0", fixEncoderSize(getBytesEncoder(), 7)],
      ["rewardsTallyScaled", getArrayEncoder(getU128Encoder(), { size: 10 })],
      [
        "rewardsIssuedUnclaimed",
        getArrayEncoder(getU64Encoder(), { size: 10 }),
      ],
      ["lastClaimTs", getArrayEncoder(getU64Encoder(), { size: 10 })],
      ["activeStakeScaled", getU128Encoder()],
      ["pendingDepositStakeScaled", getU128Encoder()],
      ["pendingDepositStakeTs", getU64Encoder()],
      ["pendingWithdrawalUnstakeScaled", getU128Encoder()],
      ["pendingWithdrawalUnstakeTs", getU64Encoder()],
      ["bump", getU64Encoder()],
      ["delegatee", getAddressEncoder()],
      ["lastStakeTs", getU64Encoder()],
      [
        "rewardsIssuedCumulative",
        getArrayEncoder(getU64Encoder(), { size: 10 }),
      ],
      ["padding1", getArrayEncoder(getU64Encoder(), { size: 40 })],
    ]),
    (value) => ({ ...value, discriminator: USER_STATE_DISCRIMINATOR }),
  );
}

/** Gets the decoder for {@link UserState} account data. */
export function getUserStateDecoder(): FixedSizeDecoder<UserState> {
  return getStructDecoder([
    ["discriminator", fixDecoderSize(getBytesDecoder(), 8)],
    ["userId", getU64Decoder()],
    ["farmState", getAddressDecoder()],
    ["owner", getAddressDecoder()],
    ["isFarmDelegated", getU8Decoder()],
    ["padding0", fixDecoderSize(getBytesDecoder(), 7)],
    ["rewardsTallyScaled", getArrayDecoder(getU128Decoder(), { size: 10 })],
    ["rewardsIssuedUnclaimed", getArrayDecoder(getU64Decoder(), { size: 10 })],
    ["lastClaimTs", getArrayDecoder(getU64Decoder(), { size: 10 })],
    ["activeStakeScaled", getU128Decoder()],
    ["pendingDepositStakeScaled", getU128Decoder()],
    ["pendingDepositStakeTs", getU64Decoder()],
    ["pendingWithdrawalUnstakeScaled", getU128Decoder()],
    ["pendingWithdrawalUnstakeTs", getU64Decoder()],
    ["bump", getU64Decoder()],
    ["delegatee", getAddressDecoder()],
    ["lastStakeTs", getU64Decoder()],
    ["rewardsIssuedCumulative", getArrayDecoder(getU64Decoder(), { size: 10 })],
    ["padding1", getArrayDecoder(getU64Decoder(), { size: 40 })],
  ]);
}

/** Gets the codec for {@link UserState} account data. */
export function getUserStateCodec(): FixedSizeCodec<UserStateArgs, UserState> {
  return combineCodec(getUserStateEncoder(), getUserStateDecoder());
}

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

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

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

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

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

export function getUserStateSize(): number {
  return 920;
}
