/**
 * 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 {
  combineCodec,
  fixDecoderSize,
  fixEncoderSize,
  getAddressEncoder,
  getBytesDecoder,
  getBytesEncoder,
  getProgramDerivedAddress,
  getStructDecoder,
  getStructEncoder,
  transformEncoder,
  type AccountMeta,
  type AccountSignerMeta,
  type Address,
  type FixedSizeCodec,
  type FixedSizeDecoder,
  type FixedSizeEncoder,
  type Instruction,
  type InstructionWithAccounts,
  type InstructionWithData,
  type ReadonlyAccount,
  type ReadonlySignerAccount,
  type ReadonlyUint8Array,
  type TransactionSigner,
  type WritableAccount,
} from "@solana/kit";
import { findClaimAccountPda, findEventAuthorityPda } from "../pdas";
import { SVM_SPOKE_PROGRAM_ADDRESS } from "../programs";
import {
  expectAddress,
  getAccountMetaFactory,
  type ResolvedAccount,
} from "../shared";

export const CLAIM_RELAYER_REFUND_DISCRIMINATOR = new Uint8Array([
  205, 34, 34, 224, 204, 103, 81, 176,
]);

export function getClaimRelayerRefundDiscriminatorBytes() {
  return fixEncoderSize(getBytesEncoder(), 8).encode(
    CLAIM_RELAYER_REFUND_DISCRIMINATOR,
  );
}

export type ClaimRelayerRefundInstruction<
  TProgram extends string = typeof SVM_SPOKE_PROGRAM_ADDRESS,
  TAccountSigner extends string | AccountMeta<string> = string,
  TAccountInitializer extends string | AccountMeta<string> = string,
  TAccountState extends string | AccountMeta<string> = string,
  TAccountVault extends string | AccountMeta<string> = string,
  TAccountMint extends string | AccountMeta<string> = string,
  TAccountRefundAddress extends string | AccountMeta<string> = string,
  TAccountTokenAccount extends string | AccountMeta<string> = string,
  TAccountClaimAccount extends string | AccountMeta<string> = string,
  TAccountTokenProgram extends string | AccountMeta<string> =
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
  TAccountEventAuthority extends string | AccountMeta<string> = string,
  TAccountProgram extends string | AccountMeta<string> = string,
  TRemainingAccounts extends readonly AccountMeta<string>[] = [],
> = Instruction<TProgram> &
  InstructionWithData<ReadonlyUint8Array> &
  InstructionWithAccounts<
    [
      TAccountSigner extends string
        ? ReadonlySignerAccount<TAccountSigner> &
            AccountSignerMeta<TAccountSigner>
        : TAccountSigner,
      TAccountInitializer extends string
        ? WritableAccount<TAccountInitializer>
        : TAccountInitializer,
      TAccountState extends string
        ? ReadonlyAccount<TAccountState>
        : TAccountState,
      TAccountVault extends string
        ? WritableAccount<TAccountVault>
        : TAccountVault,
      TAccountMint extends string
        ? ReadonlyAccount<TAccountMint>
        : TAccountMint,
      TAccountRefundAddress extends string
        ? ReadonlyAccount<TAccountRefundAddress>
        : TAccountRefundAddress,
      TAccountTokenAccount extends string
        ? WritableAccount<TAccountTokenAccount>
        : TAccountTokenAccount,
      TAccountClaimAccount extends string
        ? WritableAccount<TAccountClaimAccount>
        : TAccountClaimAccount,
      TAccountTokenProgram extends string
        ? ReadonlyAccount<TAccountTokenProgram>
        : TAccountTokenProgram,
      TAccountEventAuthority extends string
        ? ReadonlyAccount<TAccountEventAuthority>
        : TAccountEventAuthority,
      TAccountProgram extends string
        ? ReadonlyAccount<TAccountProgram>
        : TAccountProgram,
      ...TRemainingAccounts,
    ]
  >;

export type ClaimRelayerRefundInstructionData = {
  discriminator: ReadonlyUint8Array;
};

export type ClaimRelayerRefundInstructionDataArgs = {};

export function getClaimRelayerRefundInstructionDataEncoder(): FixedSizeEncoder<ClaimRelayerRefundInstructionDataArgs> {
  return transformEncoder(
    getStructEncoder([["discriminator", fixEncoderSize(getBytesEncoder(), 8)]]),
    (value) => ({
      ...value,
      discriminator: CLAIM_RELAYER_REFUND_DISCRIMINATOR,
    }),
  );
}

export function getClaimRelayerRefundInstructionDataDecoder(): FixedSizeDecoder<ClaimRelayerRefundInstructionData> {
  return getStructDecoder([
    ["discriminator", fixDecoderSize(getBytesDecoder(), 8)],
  ]);
}

export function getClaimRelayerRefundInstructionDataCodec(): FixedSizeCodec<
  ClaimRelayerRefundInstructionDataArgs,
  ClaimRelayerRefundInstructionData
> {
  return combineCodec(
    getClaimRelayerRefundInstructionDataEncoder(),
    getClaimRelayerRefundInstructionDataDecoder(),
  );
}

export type ClaimRelayerRefundAsyncInput<
  TAccountSigner extends string = string,
  TAccountInitializer extends string = string,
  TAccountState extends string = string,
  TAccountVault extends string = string,
  TAccountMint extends string = string,
  TAccountRefundAddress extends string = string,
  TAccountTokenAccount extends string = string,
  TAccountClaimAccount extends string = string,
  TAccountTokenProgram extends string = string,
  TAccountEventAuthority extends string = string,
  TAccountProgram extends string = string,
> = {
  signer: TransactionSigner<TAccountSigner>;
  initializer: Address<TAccountInitializer>;
  state: Address<TAccountState>;
  vault?: Address<TAccountVault>;
  mint: Address<TAccountMint>;
  refundAddress: Address<TAccountRefundAddress>;
  tokenAccount: Address<TAccountTokenAccount>;
  claimAccount?: Address<TAccountClaimAccount>;
  tokenProgram?: Address<TAccountTokenProgram>;
  eventAuthority?: Address<TAccountEventAuthority>;
  program: Address<TAccountProgram>;
};

export async function getClaimRelayerRefundInstructionAsync<
  TAccountSigner extends string,
  TAccountInitializer extends string,
  TAccountState extends string,
  TAccountVault extends string,
  TAccountMint extends string,
  TAccountRefundAddress extends string,
  TAccountTokenAccount extends string,
  TAccountClaimAccount extends string,
  TAccountTokenProgram extends string,
  TAccountEventAuthority extends string,
  TAccountProgram extends string,
  TProgramAddress extends Address = typeof SVM_SPOKE_PROGRAM_ADDRESS,
>(
  input: ClaimRelayerRefundAsyncInput<
    TAccountSigner,
    TAccountInitializer,
    TAccountState,
    TAccountVault,
    TAccountMint,
    TAccountRefundAddress,
    TAccountTokenAccount,
    TAccountClaimAccount,
    TAccountTokenProgram,
    TAccountEventAuthority,
    TAccountProgram
  >,
  config?: { programAddress?: TProgramAddress },
): Promise<
  ClaimRelayerRefundInstruction<
    TProgramAddress,
    TAccountSigner,
    TAccountInitializer,
    TAccountState,
    TAccountVault,
    TAccountMint,
    TAccountRefundAddress,
    TAccountTokenAccount,
    TAccountClaimAccount,
    TAccountTokenProgram,
    TAccountEventAuthority,
    TAccountProgram
  >
> {
  // Program address.
  const programAddress = config?.programAddress ?? SVM_SPOKE_PROGRAM_ADDRESS;

  // Original accounts.
  const originalAccounts = {
    signer: { value: input.signer ?? null, isWritable: false },
    initializer: { value: input.initializer ?? null, isWritable: true },
    state: { value: input.state ?? null, isWritable: false },
    vault: { value: input.vault ?? null, isWritable: true },
    mint: { value: input.mint ?? null, isWritable: false },
    refundAddress: { value: input.refundAddress ?? null, isWritable: false },
    tokenAccount: { value: input.tokenAccount ?? null, isWritable: true },
    claimAccount: { value: input.claimAccount ?? null, isWritable: true },
    tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
    eventAuthority: { value: input.eventAuthority ?? null, isWritable: false },
    program: { value: input.program ?? null, isWritable: false },
  };
  const accounts = originalAccounts as Record<
    keyof typeof originalAccounts,
    ResolvedAccount
  >;

  // Resolve default values.
  if (!accounts.tokenProgram.value) {
    accounts.tokenProgram.value =
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
  }
  if (!accounts.vault.value) {
    accounts.vault.value = await getProgramDerivedAddress({
      programAddress:
        "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" as Address<"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL">,
      seeds: [
        getAddressEncoder().encode(expectAddress(accounts.state.value)),
        getAddressEncoder().encode(expectAddress(accounts.tokenProgram.value)),
        getAddressEncoder().encode(expectAddress(accounts.mint.value)),
      ],
    });
  }
  if (!accounts.claimAccount.value) {
    accounts.claimAccount.value = await findClaimAccountPda({
      mint: expectAddress(accounts.mint.value),
      refundAddress: expectAddress(accounts.refundAddress.value),
    });
  }
  if (!accounts.eventAuthority.value) {
    accounts.eventAuthority.value = await findEventAuthorityPda();
  }

  const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
  return Object.freeze({
    accounts: [
      getAccountMeta(accounts.signer),
      getAccountMeta(accounts.initializer),
      getAccountMeta(accounts.state),
      getAccountMeta(accounts.vault),
      getAccountMeta(accounts.mint),
      getAccountMeta(accounts.refundAddress),
      getAccountMeta(accounts.tokenAccount),
      getAccountMeta(accounts.claimAccount),
      getAccountMeta(accounts.tokenProgram),
      getAccountMeta(accounts.eventAuthority),
      getAccountMeta(accounts.program),
    ],
    data: getClaimRelayerRefundInstructionDataEncoder().encode({}),
    programAddress,
  } as ClaimRelayerRefundInstruction<
    TProgramAddress,
    TAccountSigner,
    TAccountInitializer,
    TAccountState,
    TAccountVault,
    TAccountMint,
    TAccountRefundAddress,
    TAccountTokenAccount,
    TAccountClaimAccount,
    TAccountTokenProgram,
    TAccountEventAuthority,
    TAccountProgram
  >);
}

export type ClaimRelayerRefundInput<
  TAccountSigner extends string = string,
  TAccountInitializer extends string = string,
  TAccountState extends string = string,
  TAccountVault extends string = string,
  TAccountMint extends string = string,
  TAccountRefundAddress extends string = string,
  TAccountTokenAccount extends string = string,
  TAccountClaimAccount extends string = string,
  TAccountTokenProgram extends string = string,
  TAccountEventAuthority extends string = string,
  TAccountProgram extends string = string,
> = {
  signer: TransactionSigner<TAccountSigner>;
  initializer: Address<TAccountInitializer>;
  state: Address<TAccountState>;
  vault: Address<TAccountVault>;
  mint: Address<TAccountMint>;
  refundAddress: Address<TAccountRefundAddress>;
  tokenAccount: Address<TAccountTokenAccount>;
  claimAccount: Address<TAccountClaimAccount>;
  tokenProgram?: Address<TAccountTokenProgram>;
  eventAuthority: Address<TAccountEventAuthority>;
  program: Address<TAccountProgram>;
};

export function getClaimRelayerRefundInstruction<
  TAccountSigner extends string,
  TAccountInitializer extends string,
  TAccountState extends string,
  TAccountVault extends string,
  TAccountMint extends string,
  TAccountRefundAddress extends string,
  TAccountTokenAccount extends string,
  TAccountClaimAccount extends string,
  TAccountTokenProgram extends string,
  TAccountEventAuthority extends string,
  TAccountProgram extends string,
  TProgramAddress extends Address = typeof SVM_SPOKE_PROGRAM_ADDRESS,
>(
  input: ClaimRelayerRefundInput<
    TAccountSigner,
    TAccountInitializer,
    TAccountState,
    TAccountVault,
    TAccountMint,
    TAccountRefundAddress,
    TAccountTokenAccount,
    TAccountClaimAccount,
    TAccountTokenProgram,
    TAccountEventAuthority,
    TAccountProgram
  >,
  config?: { programAddress?: TProgramAddress },
): ClaimRelayerRefundInstruction<
  TProgramAddress,
  TAccountSigner,
  TAccountInitializer,
  TAccountState,
  TAccountVault,
  TAccountMint,
  TAccountRefundAddress,
  TAccountTokenAccount,
  TAccountClaimAccount,
  TAccountTokenProgram,
  TAccountEventAuthority,
  TAccountProgram
> {
  // Program address.
  const programAddress = config?.programAddress ?? SVM_SPOKE_PROGRAM_ADDRESS;

  // Original accounts.
  const originalAccounts = {
    signer: { value: input.signer ?? null, isWritable: false },
    initializer: { value: input.initializer ?? null, isWritable: true },
    state: { value: input.state ?? null, isWritable: false },
    vault: { value: input.vault ?? null, isWritable: true },
    mint: { value: input.mint ?? null, isWritable: false },
    refundAddress: { value: input.refundAddress ?? null, isWritable: false },
    tokenAccount: { value: input.tokenAccount ?? null, isWritable: true },
    claimAccount: { value: input.claimAccount ?? null, isWritable: true },
    tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
    eventAuthority: { value: input.eventAuthority ?? null, isWritable: false },
    program: { value: input.program ?? null, isWritable: false },
  };
  const accounts = originalAccounts as Record<
    keyof typeof originalAccounts,
    ResolvedAccount
  >;

  // Resolve default values.
  if (!accounts.tokenProgram.value) {
    accounts.tokenProgram.value =
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
  }

  const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
  return Object.freeze({
    accounts: [
      getAccountMeta(accounts.signer),
      getAccountMeta(accounts.initializer),
      getAccountMeta(accounts.state),
      getAccountMeta(accounts.vault),
      getAccountMeta(accounts.mint),
      getAccountMeta(accounts.refundAddress),
      getAccountMeta(accounts.tokenAccount),
      getAccountMeta(accounts.claimAccount),
      getAccountMeta(accounts.tokenProgram),
      getAccountMeta(accounts.eventAuthority),
      getAccountMeta(accounts.program),
    ],
    data: getClaimRelayerRefundInstructionDataEncoder().encode({}),
    programAddress,
  } as ClaimRelayerRefundInstruction<
    TProgramAddress,
    TAccountSigner,
    TAccountInitializer,
    TAccountState,
    TAccountVault,
    TAccountMint,
    TAccountRefundAddress,
    TAccountTokenAccount,
    TAccountClaimAccount,
    TAccountTokenProgram,
    TAccountEventAuthority,
    TAccountProgram
  >);
}

export type ParsedClaimRelayerRefundInstruction<
  TProgram extends string = typeof SVM_SPOKE_PROGRAM_ADDRESS,
  TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[],
> = {
  programAddress: Address<TProgram>;
  accounts: {
    signer: TAccountMetas[0];
    initializer: TAccountMetas[1];
    state: TAccountMetas[2];
    vault: TAccountMetas[3];
    mint: TAccountMetas[4];
    refundAddress: TAccountMetas[5];
    tokenAccount: TAccountMetas[6];
    claimAccount: TAccountMetas[7];
    tokenProgram: TAccountMetas[8];
    eventAuthority: TAccountMetas[9];
    program: TAccountMetas[10];
  };
  data: ClaimRelayerRefundInstructionData;
};

export function parseClaimRelayerRefundInstruction<
  TProgram extends string,
  TAccountMetas extends readonly AccountMeta[],
>(
  instruction: Instruction<TProgram> &
    InstructionWithAccounts<TAccountMetas> &
    InstructionWithData<ReadonlyUint8Array>,
): ParsedClaimRelayerRefundInstruction<TProgram, TAccountMetas> {
  if (instruction.accounts.length < 11) {
    // TODO: Coded error.
    throw new Error("Not enough accounts");
  }
  let accountIndex = 0;
  const getNextAccount = () => {
    const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!;
    accountIndex += 1;
    return accountMeta;
  };
  return {
    programAddress: instruction.programAddress,
    accounts: {
      signer: getNextAccount(),
      initializer: getNextAccount(),
      state: getNextAccount(),
      vault: getNextAccount(),
      mint: getNextAccount(),
      refundAddress: getNextAccount(),
      tokenAccount: getNextAccount(),
      claimAccount: getNextAccount(),
      tokenProgram: getNextAccount(),
      eventAuthority: getNextAccount(),
      program: getNextAccount(),
    },
    data: getClaimRelayerRefundInstructionDataDecoder().decode(
      instruction.data,
    ),
  };
}
