/**
 * 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,
  decodeAccount,
  fetchEncodedAccount,
  fetchEncodedAccounts,
  type Account,
  type EncodedAccount,
  type FetchAccountConfig,
  type FetchAccountsConfig,
  type MaybeAccount,
  type MaybeEncodedAccount,
} from '@solana/kit';
import { getAddressDecoder, getAddressEncoder, type Address } from '@solana/kit';
import {
  combineCodec,
  fixDecoderSize,
  fixEncoderSize,
  getArrayDecoder,
  getArrayEncoder,
  getBytesDecoder,
  getBytesEncoder,
  getStructDecoder,
  getStructEncoder,
  getU32Decoder,
  getU32Encoder,
  getU64Decoder,
  getU64Encoder,
  transformEncoder,
  type Codec,
  type Decoder,
  type Encoder,
  type ReadonlyUint8Array,
} from '@solana/kit';

export const DCA_ORDER_DISCRIMINATOR = new Uint8Array([55, 1, 103, 253, 215, 66, 27, 57]);

export function getDcaOrderDiscriminatorBytes() {
  return fixEncoderSize(getBytesEncoder(), 8).encode(DCA_ORDER_DISCRIMINATOR);
}

export type DcaOrder = {
  discriminator: ReadonlyUint8Array;
  /** User address, that created this order */
  user: Address;
  /** Guard address, that will protect this order execution */
  guard: Address;
  /** Token IN that user wants to spend */
  tokenInMint: Address;
  /** Amount of tokens IN user is willing to spend per interval/trade */
  amountInPerInterval: bigint;
  /** Minimum amount of tokens OUT to receive PER INTERVAL */
  amountOutMin: bigint;
  /** Timestamp (in seconds) when the user created and submitted the DCA order */
  startTime: number;
  /** Deadline for the operation, in Unix timestamp format, in SECONDS */
  deadline: number;
  /** Total number of intervals over which the DCA order will be executed */
  totalIntervals: number;
  /** DCA interval duration, in seconds */
  intervalDuration: number;
  /**
   * Keccak256 hash of (token_out,receiver,extra_transfers_tokens_and_receivers,secret_number)
   * Since we want to hide tokens OUT right until execution moment to protect user from
   * any sort of MEV attack
   */
  secretHash: ReadonlyUint8Array;
  /**
   * Array of requested extra transfers amounts
   * Since we want to hide tokens OUT and receivers right until execution moment
   * to protect user from any sort of MEV attack
   * Tokens and receivers will be collected from `remaining_accounts`
   * during fulfillment and checked against `secret_hash` value
   */
  extraTransfersAmounts: Array<bigint>;
  /** Total number of already executed intervals */
  totalExecutedIntervals: number;
  /** INDEX of last executed interval */
  lastExecutedIntervalIndex: number;
};

export type DcaOrderArgs = {
  /** User address, that created this order */
  user: Address;
  /** Guard address, that will protect this order execution */
  guard: Address;
  /** Token IN that user wants to spend */
  tokenInMint: Address;
  /** Amount of tokens IN user is willing to spend per interval/trade */
  amountInPerInterval: number | bigint;
  /** Minimum amount of tokens OUT to receive PER INTERVAL */
  amountOutMin: number | bigint;
  /** Timestamp (in seconds) when the user created and submitted the DCA order */
  startTime: number;
  /** Deadline for the operation, in Unix timestamp format, in SECONDS */
  deadline: number;
  /** Total number of intervals over which the DCA order will be executed */
  totalIntervals: number;
  /** DCA interval duration, in seconds */
  intervalDuration: number;
  /**
   * Keccak256 hash of (token_out,receiver,extra_transfers_tokens_and_receivers,secret_number)
   * Since we want to hide tokens OUT right until execution moment to protect user from
   * any sort of MEV attack
   */
  secretHash: ReadonlyUint8Array;
  /**
   * Array of requested extra transfers amounts
   * Since we want to hide tokens OUT and receivers right until execution moment
   * to protect user from any sort of MEV attack
   * Tokens and receivers will be collected from `remaining_accounts`
   * during fulfillment and checked against `secret_hash` value
   */
  extraTransfersAmounts: Array<number | bigint>;
  /** Total number of already executed intervals */
  totalExecutedIntervals: number;
  /** INDEX of last executed interval */
  lastExecutedIntervalIndex: number;
};

export function getDcaOrderEncoder(): Encoder<DcaOrderArgs> {
  return transformEncoder(
    getStructEncoder([
      ['discriminator', fixEncoderSize(getBytesEncoder(), 8)],
      ['user', getAddressEncoder()],
      ['guard', getAddressEncoder()],
      ['tokenInMint', getAddressEncoder()],
      ['amountInPerInterval', getU64Encoder()],
      ['amountOutMin', getU64Encoder()],
      ['startTime', getU32Encoder()],
      ['deadline', getU32Encoder()],
      ['totalIntervals', getU32Encoder()],
      ['intervalDuration', getU32Encoder()],
      ['secretHash', fixEncoderSize(getBytesEncoder(), 32)],
      ['extraTransfersAmounts', getArrayEncoder(getU64Encoder())],
      ['totalExecutedIntervals', getU32Encoder()],
      ['lastExecutedIntervalIndex', getU32Encoder()],
    ]),
    (value) => ({ ...value, discriminator: DCA_ORDER_DISCRIMINATOR }),
  );
}

export function getDcaOrderDecoder(): Decoder<DcaOrder> {
  return getStructDecoder([
    ['discriminator', fixDecoderSize(getBytesDecoder(), 8)],
    ['user', getAddressDecoder()],
    ['guard', getAddressDecoder()],
    ['tokenInMint', getAddressDecoder()],
    ['amountInPerInterval', getU64Decoder()],
    ['amountOutMin', getU64Decoder()],
    ['startTime', getU32Decoder()],
    ['deadline', getU32Decoder()],
    ['totalIntervals', getU32Decoder()],
    ['intervalDuration', getU32Decoder()],
    ['secretHash', fixDecoderSize(getBytesDecoder(), 32)],
    ['extraTransfersAmounts', getArrayDecoder(getU64Decoder())],
    ['totalExecutedIntervals', getU32Decoder()],
    ['lastExecutedIntervalIndex', getU32Decoder()],
  ]);
}

export function getDcaOrderCodec(): Codec<DcaOrderArgs, DcaOrder> {
  return combineCodec(getDcaOrderEncoder(), getDcaOrderDecoder());
}

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

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

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

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

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