/**
 * 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,
  getArrayDecoder,
  getArrayEncoder,
  getBytesDecoder,
  getBytesEncoder,
  getOptionDecoder,
  getOptionEncoder,
  getProgramDerivedAddress,
  getStructDecoder,
  getStructEncoder,
  getU32Decoder,
  getU32Encoder,
  transformEncoder,
  type AccountMeta,
  type AccountSignerMeta,
  type Address,
  type Codec,
  type Decoder,
  type Encoder,
  type Instruction,
  type InstructionWithAccounts,
  type InstructionWithData,
  type Option,
  type OptionOrNullable,
  type ReadonlyAccount,
  type ReadonlySignerAccount,
  type ReadonlyUint8Array,
  type TransactionSigner,
  type WritableAccount,
} from "@solana/kit";
import {
  findEventAuthorityPda,
  findFillStatusPda,
  findInstructionParamsPda,
} from "../pdas";
import { SVM_SPOKE_PROGRAM_ADDRESS } from "../programs";
import {
  expectAddress,
  expectSome,
  getAccountMetaFactory,
  type ResolvedAccount,
} from "../shared";
import {
  getSlowFillDecoder,
  getSlowFillEncoder,
  type SlowFill,
  type SlowFillArgs,
} from "../types";

export const EXECUTE_SLOW_RELAY_LEAF_DISCRIMINATOR = new Uint8Array([
  26, 207, 3, 168, 193, 252, 59, 127,
]);

export function getExecuteSlowRelayLeafDiscriminatorBytes() {
  return fixEncoderSize(getBytesEncoder(), 8).encode(
    EXECUTE_SLOW_RELAY_LEAF_DISCRIMINATOR,
  );
}

export type ExecuteSlowRelayLeafInstruction<
  TProgram extends string = typeof SVM_SPOKE_PROGRAM_ADDRESS,
  TAccountSigner extends string | AccountMeta<string> = string,
  TAccountInstructionParams extends string | AccountMeta<string> = string,
  TAccountState extends string | AccountMeta<string> = string,
  TAccountRootBundle extends string | AccountMeta<string> = string,
  TAccountFillStatus extends string | AccountMeta<string> = string,
  TAccountMint extends string | AccountMeta<string> = string,
  TAccountRecipientTokenAccount extends string | AccountMeta<string> = string,
  TAccountVault extends string | AccountMeta<string> = string,
  TAccountTokenProgram extends string | AccountMeta<string> =
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
  TAccountSystemProgram extends string | AccountMeta<string> =
    "11111111111111111111111111111111",
  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,
      TAccountInstructionParams extends string
        ? WritableAccount<TAccountInstructionParams>
        : TAccountInstructionParams,
      TAccountState extends string
        ? ReadonlyAccount<TAccountState>
        : TAccountState,
      TAccountRootBundle extends string
        ? ReadonlyAccount<TAccountRootBundle>
        : TAccountRootBundle,
      TAccountFillStatus extends string
        ? WritableAccount<TAccountFillStatus>
        : TAccountFillStatus,
      TAccountMint extends string
        ? ReadonlyAccount<TAccountMint>
        : TAccountMint,
      TAccountRecipientTokenAccount extends string
        ? WritableAccount<TAccountRecipientTokenAccount>
        : TAccountRecipientTokenAccount,
      TAccountVault extends string
        ? WritableAccount<TAccountVault>
        : TAccountVault,
      TAccountTokenProgram extends string
        ? ReadonlyAccount<TAccountTokenProgram>
        : TAccountTokenProgram,
      TAccountSystemProgram extends string
        ? ReadonlyAccount<TAccountSystemProgram>
        : TAccountSystemProgram,
      TAccountEventAuthority extends string
        ? ReadonlyAccount<TAccountEventAuthority>
        : TAccountEventAuthority,
      TAccountProgram extends string
        ? ReadonlyAccount<TAccountProgram>
        : TAccountProgram,
      ...TRemainingAccounts,
    ]
  >;

export type ExecuteSlowRelayLeafInstructionData = {
  discriminator: ReadonlyUint8Array;
  relayHash: ReadonlyUint8Array;
  slowFillLeaf: Option<SlowFill>;
  rootBundleId: Option<number>;
  proof: Option<Array<ReadonlyUint8Array>>;
};

export type ExecuteSlowRelayLeafInstructionDataArgs = {
  relayHash: ReadonlyUint8Array;
  slowFillLeaf: OptionOrNullable<SlowFillArgs>;
  rootBundleId: OptionOrNullable<number>;
  proof: OptionOrNullable<Array<ReadonlyUint8Array>>;
};

export function getExecuteSlowRelayLeafInstructionDataEncoder(): Encoder<ExecuteSlowRelayLeafInstructionDataArgs> {
  return transformEncoder(
    getStructEncoder([
      ["discriminator", fixEncoderSize(getBytesEncoder(), 8)],
      ["relayHash", fixEncoderSize(getBytesEncoder(), 32)],
      ["slowFillLeaf", getOptionEncoder(getSlowFillEncoder())],
      ["rootBundleId", getOptionEncoder(getU32Encoder())],
      [
        "proof",
        getOptionEncoder(
          getArrayEncoder(fixEncoderSize(getBytesEncoder(), 32)),
        ),
      ],
    ]),
    (value) => ({
      ...value,
      discriminator: EXECUTE_SLOW_RELAY_LEAF_DISCRIMINATOR,
    }),
  );
}

export function getExecuteSlowRelayLeafInstructionDataDecoder(): Decoder<ExecuteSlowRelayLeafInstructionData> {
  return getStructDecoder([
    ["discriminator", fixDecoderSize(getBytesDecoder(), 8)],
    ["relayHash", fixDecoderSize(getBytesDecoder(), 32)],
    ["slowFillLeaf", getOptionDecoder(getSlowFillDecoder())],
    ["rootBundleId", getOptionDecoder(getU32Decoder())],
    [
      "proof",
      getOptionDecoder(getArrayDecoder(fixDecoderSize(getBytesDecoder(), 32))),
    ],
  ]);
}

export function getExecuteSlowRelayLeafInstructionDataCodec(): Codec<
  ExecuteSlowRelayLeafInstructionDataArgs,
  ExecuteSlowRelayLeafInstructionData
> {
  return combineCodec(
    getExecuteSlowRelayLeafInstructionDataEncoder(),
    getExecuteSlowRelayLeafInstructionDataDecoder(),
  );
}

export type ExecuteSlowRelayLeafAsyncInput<
  TAccountSigner extends string = string,
  TAccountInstructionParams extends string = string,
  TAccountState extends string = string,
  TAccountRootBundle extends string = string,
  TAccountFillStatus extends string = string,
  TAccountMint extends string = string,
  TAccountRecipientTokenAccount extends string = string,
  TAccountVault extends string = string,
  TAccountTokenProgram extends string = string,
  TAccountSystemProgram extends string = string,
  TAccountEventAuthority extends string = string,
  TAccountProgram extends string = string,
> = {
  signer: TransactionSigner<TAccountSigner>;
  instructionParams?: Address<TAccountInstructionParams>;
  state: Address<TAccountState>;
  rootBundle: Address<TAccountRootBundle>;
  fillStatus?: Address<TAccountFillStatus>;
  mint: Address<TAccountMint>;
  recipientTokenAccount?: Address<TAccountRecipientTokenAccount>;
  vault?: Address<TAccountVault>;
  tokenProgram?: Address<TAccountTokenProgram>;
  systemProgram?: Address<TAccountSystemProgram>;
  eventAuthority?: Address<TAccountEventAuthority>;
  program: Address<TAccountProgram>;
  relayHash: ExecuteSlowRelayLeafInstructionDataArgs["relayHash"];
  slowFillLeaf: ExecuteSlowRelayLeafInstructionDataArgs["slowFillLeaf"];
  rootBundleId: ExecuteSlowRelayLeafInstructionDataArgs["rootBundleId"];
  proof: ExecuteSlowRelayLeafInstructionDataArgs["proof"];
};

export async function getExecuteSlowRelayLeafInstructionAsync<
  TAccountSigner extends string,
  TAccountInstructionParams extends string,
  TAccountState extends string,
  TAccountRootBundle extends string,
  TAccountFillStatus extends string,
  TAccountMint extends string,
  TAccountRecipientTokenAccount extends string,
  TAccountVault extends string,
  TAccountTokenProgram extends string,
  TAccountSystemProgram extends string,
  TAccountEventAuthority extends string,
  TAccountProgram extends string,
  TProgramAddress extends Address = typeof SVM_SPOKE_PROGRAM_ADDRESS,
>(
  input: ExecuteSlowRelayLeafAsyncInput<
    TAccountSigner,
    TAccountInstructionParams,
    TAccountState,
    TAccountRootBundle,
    TAccountFillStatus,
    TAccountMint,
    TAccountRecipientTokenAccount,
    TAccountVault,
    TAccountTokenProgram,
    TAccountSystemProgram,
    TAccountEventAuthority,
    TAccountProgram
  >,
  config?: { programAddress?: TProgramAddress },
): Promise<
  ExecuteSlowRelayLeafInstruction<
    TProgramAddress,
    TAccountSigner,
    TAccountInstructionParams,
    TAccountState,
    TAccountRootBundle,
    TAccountFillStatus,
    TAccountMint,
    TAccountRecipientTokenAccount,
    TAccountVault,
    TAccountTokenProgram,
    TAccountSystemProgram,
    TAccountEventAuthority,
    TAccountProgram
  >
> {
  // Program address.
  const programAddress = config?.programAddress ?? SVM_SPOKE_PROGRAM_ADDRESS;

  // Original accounts.
  const originalAccounts = {
    signer: { value: input.signer ?? null, isWritable: false },
    instructionParams: {
      value: input.instructionParams ?? null,
      isWritable: true,
    },
    state: { value: input.state ?? null, isWritable: false },
    rootBundle: { value: input.rootBundle ?? null, isWritable: false },
    fillStatus: { value: input.fillStatus ?? null, isWritable: true },
    mint: { value: input.mint ?? null, isWritable: false },
    recipientTokenAccount: {
      value: input.recipientTokenAccount ?? null,
      isWritable: true,
    },
    vault: { value: input.vault ?? null, isWritable: true },
    tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
    systemProgram: { value: input.systemProgram ?? 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
  >;

  // Original args.
  const args = { ...input };

  // Resolve default values.
  if (!accounts.instructionParams.value) {
    accounts.instructionParams.value = await findInstructionParamsPda({
      signer: expectAddress(accounts.signer.value),
    });
  }
  if (!accounts.fillStatus.value) {
    accounts.fillStatus.value = await findFillStatusPda({
      relayHash: expectSome(args.relayHash),
    });
  }
  if (!accounts.tokenProgram.value) {
    accounts.tokenProgram.value =
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
  }
  if (!accounts.recipientTokenAccount.value) {
    accounts.recipientTokenAccount.value = await getProgramDerivedAddress({
      programAddress:
        "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" as Address<"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL">,
      seeds: [
        getOptionEncoder(getSlowFillEncoder()).encode(
          expectSome(args.slowFillLeaf),
        ),
        getAddressEncoder().encode(expectAddress(accounts.tokenProgram.value)),
        getAddressEncoder().encode(expectAddress(accounts.mint.value)),
      ],
    });
  }
  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.systemProgram.value) {
    accounts.systemProgram.value =
      "11111111111111111111111111111111" as Address<"11111111111111111111111111111111">;
  }
  if (!accounts.eventAuthority.value) {
    accounts.eventAuthority.value = await findEventAuthorityPda();
  }

  const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
  return Object.freeze({
    accounts: [
      getAccountMeta(accounts.signer),
      getAccountMeta(accounts.instructionParams),
      getAccountMeta(accounts.state),
      getAccountMeta(accounts.rootBundle),
      getAccountMeta(accounts.fillStatus),
      getAccountMeta(accounts.mint),
      getAccountMeta(accounts.recipientTokenAccount),
      getAccountMeta(accounts.vault),
      getAccountMeta(accounts.tokenProgram),
      getAccountMeta(accounts.systemProgram),
      getAccountMeta(accounts.eventAuthority),
      getAccountMeta(accounts.program),
    ],
    data: getExecuteSlowRelayLeafInstructionDataEncoder().encode(
      args as ExecuteSlowRelayLeafInstructionDataArgs,
    ),
    programAddress,
  } as ExecuteSlowRelayLeafInstruction<
    TProgramAddress,
    TAccountSigner,
    TAccountInstructionParams,
    TAccountState,
    TAccountRootBundle,
    TAccountFillStatus,
    TAccountMint,
    TAccountRecipientTokenAccount,
    TAccountVault,
    TAccountTokenProgram,
    TAccountSystemProgram,
    TAccountEventAuthority,
    TAccountProgram
  >);
}

export type ExecuteSlowRelayLeafInput<
  TAccountSigner extends string = string,
  TAccountInstructionParams extends string = string,
  TAccountState extends string = string,
  TAccountRootBundle extends string = string,
  TAccountFillStatus extends string = string,
  TAccountMint extends string = string,
  TAccountRecipientTokenAccount extends string = string,
  TAccountVault extends string = string,
  TAccountTokenProgram extends string = string,
  TAccountSystemProgram extends string = string,
  TAccountEventAuthority extends string = string,
  TAccountProgram extends string = string,
> = {
  signer: TransactionSigner<TAccountSigner>;
  instructionParams?: Address<TAccountInstructionParams>;
  state: Address<TAccountState>;
  rootBundle: Address<TAccountRootBundle>;
  fillStatus: Address<TAccountFillStatus>;
  mint: Address<TAccountMint>;
  recipientTokenAccount: Address<TAccountRecipientTokenAccount>;
  vault: Address<TAccountVault>;
  tokenProgram?: Address<TAccountTokenProgram>;
  systemProgram?: Address<TAccountSystemProgram>;
  eventAuthority: Address<TAccountEventAuthority>;
  program: Address<TAccountProgram>;
  relayHash: ExecuteSlowRelayLeafInstructionDataArgs["relayHash"];
  slowFillLeaf: ExecuteSlowRelayLeafInstructionDataArgs["slowFillLeaf"];
  rootBundleId: ExecuteSlowRelayLeafInstructionDataArgs["rootBundleId"];
  proof: ExecuteSlowRelayLeafInstructionDataArgs["proof"];
};

export function getExecuteSlowRelayLeafInstruction<
  TAccountSigner extends string,
  TAccountInstructionParams extends string,
  TAccountState extends string,
  TAccountRootBundle extends string,
  TAccountFillStatus extends string,
  TAccountMint extends string,
  TAccountRecipientTokenAccount extends string,
  TAccountVault extends string,
  TAccountTokenProgram extends string,
  TAccountSystemProgram extends string,
  TAccountEventAuthority extends string,
  TAccountProgram extends string,
  TProgramAddress extends Address = typeof SVM_SPOKE_PROGRAM_ADDRESS,
>(
  input: ExecuteSlowRelayLeafInput<
    TAccountSigner,
    TAccountInstructionParams,
    TAccountState,
    TAccountRootBundle,
    TAccountFillStatus,
    TAccountMint,
    TAccountRecipientTokenAccount,
    TAccountVault,
    TAccountTokenProgram,
    TAccountSystemProgram,
    TAccountEventAuthority,
    TAccountProgram
  >,
  config?: { programAddress?: TProgramAddress },
): ExecuteSlowRelayLeafInstruction<
  TProgramAddress,
  TAccountSigner,
  TAccountInstructionParams,
  TAccountState,
  TAccountRootBundle,
  TAccountFillStatus,
  TAccountMint,
  TAccountRecipientTokenAccount,
  TAccountVault,
  TAccountTokenProgram,
  TAccountSystemProgram,
  TAccountEventAuthority,
  TAccountProgram
> {
  // Program address.
  const programAddress = config?.programAddress ?? SVM_SPOKE_PROGRAM_ADDRESS;

  // Original accounts.
  const originalAccounts = {
    signer: { value: input.signer ?? null, isWritable: false },
    instructionParams: {
      value: input.instructionParams ?? null,
      isWritable: true,
    },
    state: { value: input.state ?? null, isWritable: false },
    rootBundle: { value: input.rootBundle ?? null, isWritable: false },
    fillStatus: { value: input.fillStatus ?? null, isWritable: true },
    mint: { value: input.mint ?? null, isWritable: false },
    recipientTokenAccount: {
      value: input.recipientTokenAccount ?? null,
      isWritable: true,
    },
    vault: { value: input.vault ?? null, isWritable: true },
    tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
    systemProgram: { value: input.systemProgram ?? 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
  >;

  // Original args.
  const args = { ...input };

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

  const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
  return Object.freeze({
    accounts: [
      getAccountMeta(accounts.signer),
      getAccountMeta(accounts.instructionParams),
      getAccountMeta(accounts.state),
      getAccountMeta(accounts.rootBundle),
      getAccountMeta(accounts.fillStatus),
      getAccountMeta(accounts.mint),
      getAccountMeta(accounts.recipientTokenAccount),
      getAccountMeta(accounts.vault),
      getAccountMeta(accounts.tokenProgram),
      getAccountMeta(accounts.systemProgram),
      getAccountMeta(accounts.eventAuthority),
      getAccountMeta(accounts.program),
    ],
    data: getExecuteSlowRelayLeafInstructionDataEncoder().encode(
      args as ExecuteSlowRelayLeafInstructionDataArgs,
    ),
    programAddress,
  } as ExecuteSlowRelayLeafInstruction<
    TProgramAddress,
    TAccountSigner,
    TAccountInstructionParams,
    TAccountState,
    TAccountRootBundle,
    TAccountFillStatus,
    TAccountMint,
    TAccountRecipientTokenAccount,
    TAccountVault,
    TAccountTokenProgram,
    TAccountSystemProgram,
    TAccountEventAuthority,
    TAccountProgram
  >);
}

export type ParsedExecuteSlowRelayLeafInstruction<
  TProgram extends string = typeof SVM_SPOKE_PROGRAM_ADDRESS,
  TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[],
> = {
  programAddress: Address<TProgram>;
  accounts: {
    signer: TAccountMetas[0];
    instructionParams?: TAccountMetas[1] | undefined;
    state: TAccountMetas[2];
    rootBundle: TAccountMetas[3];
    fillStatus: TAccountMetas[4];
    mint: TAccountMetas[5];
    recipientTokenAccount: TAccountMetas[6];
    vault: TAccountMetas[7];
    tokenProgram: TAccountMetas[8];
    systemProgram: TAccountMetas[9];
    eventAuthority: TAccountMetas[10];
    program: TAccountMetas[11];
  };
  data: ExecuteSlowRelayLeafInstructionData;
};

export function parseExecuteSlowRelayLeafInstruction<
  TProgram extends string,
  TAccountMetas extends readonly AccountMeta[],
>(
  instruction: Instruction<TProgram> &
    InstructionWithAccounts<TAccountMetas> &
    InstructionWithData<ReadonlyUint8Array>,
): ParsedExecuteSlowRelayLeafInstruction<TProgram, TAccountMetas> {
  if (instruction.accounts.length < 12) {
    // 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;
  };
  const getNextOptionalAccount = () => {
    const accountMeta = getNextAccount();
    return accountMeta.address === SVM_SPOKE_PROGRAM_ADDRESS
      ? undefined
      : accountMeta;
  };
  return {
    programAddress: instruction.programAddress,
    accounts: {
      signer: getNextAccount(),
      instructionParams: getNextOptionalAccount(),
      state: getNextAccount(),
      rootBundle: getNextAccount(),
      fillStatus: getNextAccount(),
      mint: getNextAccount(),
      recipientTokenAccount: getNextAccount(),
      vault: getNextAccount(),
      tokenProgram: getNextAccount(),
      systemProgram: getNextAccount(),
      eventAuthority: getNextAccount(),
      program: getNextAccount(),
    },
    data: getExecuteSlowRelayLeafInstructionDataDecoder().decode(
      instruction.data,
    ),
  };
}
