/**
 * 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,
  getAddressEncoder,
  getProgramDerivedAddress,
  getStructDecoder,
  getStructEncoder,
  getU8Decoder,
  getU8Encoder,
  SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS,
  SolanaError,
  transformEncoder,
  type AccountMeta,
  type AccountSignerMeta,
  type Address,
  type FixedSizeCodec,
  type FixedSizeDecoder,
  type FixedSizeEncoder,
  type Instruction,
  type InstructionWithAccounts,
  type InstructionWithData,
  type ProgramDerivedAddress,
  type ReadonlyAccount,
  type ReadonlySignerAccount,
  type ReadonlyUint8Array,
  type TransactionSigner,
  type WritableAccount,
  type WritableSignerAccount,
} from "@solana/kit";
import {
  getAccountMetaFactory,
  getAddressFromResolvedInstructionAccount,
  getResolvedInstructionAccountAsProgramDerivedAddress,
  type ResolvedInstructionAccount,
} from "@solana/kit/program-client-core";
import { findAllowedMintPda, findEventAuthorityPda } from "../pdas";
import { PRIVATE_CHANNEL_ESCROW_PROGRAM_PROGRAM_ADDRESS } from "../programs";

export const ALLOW_MINT_DISCRIMINATOR = 1;

export function getAllowMintDiscriminatorBytes(): ReadonlyUint8Array {
  return getU8Encoder().encode(ALLOW_MINT_DISCRIMINATOR);
}

export type AllowMintInstruction<
  TProgram extends string =
    typeof PRIVATE_CHANNEL_ESCROW_PROGRAM_PROGRAM_ADDRESS,
  TAccountPayer extends string | AccountMeta<string> = string,
  TAccountAdmin extends string | AccountMeta<string> = string,
  TAccountInstance extends string | AccountMeta<string> = string,
  TAccountMint extends string | AccountMeta<string> = string,
  TAccountAllowedMint extends string | AccountMeta<string> = string,
  TAccountInstanceAta extends string | AccountMeta<string> = string,
  TAccountSystemProgram extends string | AccountMeta<string> =
    "11111111111111111111111111111111",
  TAccountTokenProgram extends string | AccountMeta<string> =
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
  TAccountAssociatedTokenProgram extends string | AccountMeta<string> =
    "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
  TAccountEventAuthority extends string | AccountMeta<string> = string,
  TAccountPrivateChannelEscrowProgram extends string | AccountMeta<string> =
    "9tgHa1DcnaSSUtmMsst8ovKTe1Gfxzezn27KnH9xXYeU",
  TRemainingAccounts extends readonly AccountMeta<string>[] = [],
> = Instruction<TProgram> &
  InstructionWithData<ReadonlyUint8Array> &
  InstructionWithAccounts<
    [
      TAccountPayer extends string
        ? WritableSignerAccount<TAccountPayer> &
            AccountSignerMeta<TAccountPayer>
        : TAccountPayer,
      TAccountAdmin extends string
        ? ReadonlySignerAccount<TAccountAdmin> &
            AccountSignerMeta<TAccountAdmin>
        : TAccountAdmin,
      TAccountInstance extends string
        ? ReadonlyAccount<TAccountInstance>
        : TAccountInstance,
      TAccountMint extends string
        ? ReadonlyAccount<TAccountMint>
        : TAccountMint,
      TAccountAllowedMint extends string
        ? WritableAccount<TAccountAllowedMint>
        : TAccountAllowedMint,
      TAccountInstanceAta extends string
        ? WritableAccount<TAccountInstanceAta>
        : TAccountInstanceAta,
      TAccountSystemProgram extends string
        ? ReadonlyAccount<TAccountSystemProgram>
        : TAccountSystemProgram,
      TAccountTokenProgram extends string
        ? ReadonlyAccount<TAccountTokenProgram>
        : TAccountTokenProgram,
      TAccountAssociatedTokenProgram extends string
        ? ReadonlyAccount<TAccountAssociatedTokenProgram>
        : TAccountAssociatedTokenProgram,
      TAccountEventAuthority extends string
        ? ReadonlyAccount<TAccountEventAuthority>
        : TAccountEventAuthority,
      TAccountPrivateChannelEscrowProgram extends string
        ? ReadonlyAccount<TAccountPrivateChannelEscrowProgram>
        : TAccountPrivateChannelEscrowProgram,
      ...TRemainingAccounts,
    ]
  >;

export type AllowMintInstructionData = { discriminator: number; bump: number };

export type AllowMintInstructionDataArgs = { bump: number };

export function getAllowMintInstructionDataEncoder(): FixedSizeEncoder<AllowMintInstructionDataArgs> {
  return transformEncoder(
    getStructEncoder([
      ["discriminator", getU8Encoder()],
      ["bump", getU8Encoder()],
    ]),
    (value) => ({ ...value, discriminator: ALLOW_MINT_DISCRIMINATOR }),
  );
}

export function getAllowMintInstructionDataDecoder(): FixedSizeDecoder<AllowMintInstructionData> {
  return getStructDecoder([
    ["discriminator", getU8Decoder()],
    ["bump", getU8Decoder()],
  ]);
}

export function getAllowMintInstructionDataCodec(): FixedSizeCodec<
  AllowMintInstructionDataArgs,
  AllowMintInstructionData
> {
  return combineCodec(
    getAllowMintInstructionDataEncoder(),
    getAllowMintInstructionDataDecoder(),
  );
}

export type AllowMintAsyncInput<
  TAccountPayer extends string = string,
  TAccountAdmin extends string = string,
  TAccountInstance extends string = string,
  TAccountMint extends string = string,
  TAccountAllowedMint extends string = string,
  TAccountInstanceAta extends string = string,
  TAccountSystemProgram extends string = string,
  TAccountTokenProgram extends string = string,
  TAccountAssociatedTokenProgram extends string = string,
  TAccountEventAuthority extends string = string,
  TAccountPrivateChannelEscrowProgram extends string = string,
> = {
  /** Transaction fee payer */
  payer: TransactionSigner<TAccountPayer>;
  /** Admin of Instance */
  admin: TransactionSigner<TAccountAdmin>;
  /** Instance PDA to validate admin authority */
  instance: Address<TAccountInstance>;
  /** Token mint to be allowed */
  mint: Address<TAccountMint>;
  /** PDA of the Allowed Mint */
  allowedMint?: ProgramDerivedAddress<TAccountAllowedMint>;
  /** Instance Escrow account for specified mint */
  instanceAta?: Address<TAccountInstanceAta>;
  /** System program */
  systemProgram?: Address<TAccountSystemProgram>;
  /** Token program */
  tokenProgram?: Address<TAccountTokenProgram>;
  /** Associated Token program */
  associatedTokenProgram?: Address<TAccountAssociatedTokenProgram>;
  /** Event authority PDA for emitting events */
  eventAuthority?: Address<TAccountEventAuthority>;
  /** Current program for CPI */
  privateChannelEscrowProgram?: Address<TAccountPrivateChannelEscrowProgram>;
  bump?: AllowMintInstructionDataArgs["bump"];
};

export async function getAllowMintInstructionAsync<
  TAccountPayer extends string,
  TAccountAdmin extends string,
  TAccountInstance extends string,
  TAccountMint extends string,
  TAccountAllowedMint extends string,
  TAccountInstanceAta extends string,
  TAccountSystemProgram extends string,
  TAccountTokenProgram extends string,
  TAccountAssociatedTokenProgram extends string,
  TAccountEventAuthority extends string,
  TAccountPrivateChannelEscrowProgram extends string,
  TProgramAddress extends Address =
    typeof PRIVATE_CHANNEL_ESCROW_PROGRAM_PROGRAM_ADDRESS,
>(
  input: AllowMintAsyncInput<
    TAccountPayer,
    TAccountAdmin,
    TAccountInstance,
    TAccountMint,
    TAccountAllowedMint,
    TAccountInstanceAta,
    TAccountSystemProgram,
    TAccountTokenProgram,
    TAccountAssociatedTokenProgram,
    TAccountEventAuthority,
    TAccountPrivateChannelEscrowProgram
  >,
  config?: { programAddress?: TProgramAddress },
): Promise<
  AllowMintInstruction<
    TProgramAddress,
    TAccountPayer,
    TAccountAdmin,
    TAccountInstance,
    TAccountMint,
    TAccountAllowedMint,
    TAccountInstanceAta,
    TAccountSystemProgram,
    TAccountTokenProgram,
    TAccountAssociatedTokenProgram,
    TAccountEventAuthority,
    TAccountPrivateChannelEscrowProgram
  >
> {
  // Program address.
  const programAddress =
    config?.programAddress ?? PRIVATE_CHANNEL_ESCROW_PROGRAM_PROGRAM_ADDRESS;

  // Original accounts.
  const originalAccounts = {
    payer: { value: input.payer ?? null, isWritable: true },
    admin: { value: input.admin ?? null, isWritable: false },
    instance: { value: input.instance ?? null, isWritable: false },
    mint: { value: input.mint ?? null, isWritable: false },
    allowedMint: { value: input.allowedMint ?? null, isWritable: true },
    instanceAta: { value: input.instanceAta ?? null, isWritable: true },
    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
    tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
    associatedTokenProgram: {
      value: input.associatedTokenProgram ?? null,
      isWritable: false,
    },
    eventAuthority: { value: input.eventAuthority ?? null, isWritable: false },
    privateChannelEscrowProgram: {
      value: input.privateChannelEscrowProgram ?? null,
      isWritable: false,
    },
  };
  const accounts = originalAccounts as Record<
    keyof typeof originalAccounts,
    ResolvedInstructionAccount
  >;

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

  // Resolve default values.
  if (!accounts.allowedMint.value) {
    accounts.allowedMint.value = await findAllowedMintPda({
      instance: getAddressFromResolvedInstructionAccount(
        "instance",
        accounts.instance.value,
      ),
      mint: getAddressFromResolvedInstructionAccount(
        "mint",
        accounts.mint.value,
      ),
    });
  }
  if (!accounts.tokenProgram.value) {
    accounts.tokenProgram.value =
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
  }
  if (!accounts.instanceAta.value) {
    accounts.instanceAta.value = await getProgramDerivedAddress({
      programAddress:
        "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" as Address<"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL">,
      seeds: [
        getAddressEncoder().encode(
          getAddressFromResolvedInstructionAccount(
            "instance",
            accounts.instance.value,
          ),
        ),
        getAddressEncoder().encode(
          getAddressFromResolvedInstructionAccount(
            "tokenProgram",
            accounts.tokenProgram.value,
          ),
        ),
        getAddressEncoder().encode(
          getAddressFromResolvedInstructionAccount("mint", accounts.mint.value),
        ),
      ],
    });
  }
  if (!accounts.systemProgram.value) {
    accounts.systemProgram.value =
      "11111111111111111111111111111111" as Address<"11111111111111111111111111111111">;
  }
  if (!accounts.associatedTokenProgram.value) {
    accounts.associatedTokenProgram.value =
      "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" as Address<"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL">;
  }
  if (!accounts.eventAuthority.value) {
    accounts.eventAuthority.value = await findEventAuthorityPda();
  }
  if (!accounts.privateChannelEscrowProgram.value) {
    accounts.privateChannelEscrowProgram.value =
      "9tgHa1DcnaSSUtmMsst8ovKTe1Gfxzezn27KnH9xXYeU" as Address<"9tgHa1DcnaSSUtmMsst8ovKTe1Gfxzezn27KnH9xXYeU">;
  }
  if (!args.bump) {
    args.bump = getResolvedInstructionAccountAsProgramDerivedAddress(
      "allowedMint",
      accounts.allowedMint.value,
    )[1];
  }

  const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
  return Object.freeze({
    accounts: [
      getAccountMeta("payer", accounts.payer),
      getAccountMeta("admin", accounts.admin),
      getAccountMeta("instance", accounts.instance),
      getAccountMeta("mint", accounts.mint),
      getAccountMeta("allowedMint", accounts.allowedMint),
      getAccountMeta("instanceAta", accounts.instanceAta),
      getAccountMeta("systemProgram", accounts.systemProgram),
      getAccountMeta("tokenProgram", accounts.tokenProgram),
      getAccountMeta("associatedTokenProgram", accounts.associatedTokenProgram),
      getAccountMeta("eventAuthority", accounts.eventAuthority),
      getAccountMeta(
        "privateChannelEscrowProgram",
        accounts.privateChannelEscrowProgram,
      ),
    ],
    data: getAllowMintInstructionDataEncoder().encode(
      args as AllowMintInstructionDataArgs,
    ),
    programAddress,
  } as AllowMintInstruction<
    TProgramAddress,
    TAccountPayer,
    TAccountAdmin,
    TAccountInstance,
    TAccountMint,
    TAccountAllowedMint,
    TAccountInstanceAta,
    TAccountSystemProgram,
    TAccountTokenProgram,
    TAccountAssociatedTokenProgram,
    TAccountEventAuthority,
    TAccountPrivateChannelEscrowProgram
  >);
}

export type AllowMintInput<
  TAccountPayer extends string = string,
  TAccountAdmin extends string = string,
  TAccountInstance extends string = string,
  TAccountMint extends string = string,
  TAccountAllowedMint extends string = string,
  TAccountInstanceAta extends string = string,
  TAccountSystemProgram extends string = string,
  TAccountTokenProgram extends string = string,
  TAccountAssociatedTokenProgram extends string = string,
  TAccountEventAuthority extends string = string,
  TAccountPrivateChannelEscrowProgram extends string = string,
> = {
  /** Transaction fee payer */
  payer: TransactionSigner<TAccountPayer>;
  /** Admin of Instance */
  admin: TransactionSigner<TAccountAdmin>;
  /** Instance PDA to validate admin authority */
  instance: Address<TAccountInstance>;
  /** Token mint to be allowed */
  mint: Address<TAccountMint>;
  /** PDA of the Allowed Mint */
  allowedMint: ProgramDerivedAddress<TAccountAllowedMint>;
  /** Instance Escrow account for specified mint */
  instanceAta: Address<TAccountInstanceAta>;
  /** System program */
  systemProgram?: Address<TAccountSystemProgram>;
  /** Token program */
  tokenProgram?: Address<TAccountTokenProgram>;
  /** Associated Token program */
  associatedTokenProgram?: Address<TAccountAssociatedTokenProgram>;
  /** Event authority PDA for emitting events */
  eventAuthority: Address<TAccountEventAuthority>;
  /** Current program for CPI */
  privateChannelEscrowProgram?: Address<TAccountPrivateChannelEscrowProgram>;
  bump?: AllowMintInstructionDataArgs["bump"];
};

export function getAllowMintInstruction<
  TAccountPayer extends string,
  TAccountAdmin extends string,
  TAccountInstance extends string,
  TAccountMint extends string,
  TAccountAllowedMint extends string,
  TAccountInstanceAta extends string,
  TAccountSystemProgram extends string,
  TAccountTokenProgram extends string,
  TAccountAssociatedTokenProgram extends string,
  TAccountEventAuthority extends string,
  TAccountPrivateChannelEscrowProgram extends string,
  TProgramAddress extends Address =
    typeof PRIVATE_CHANNEL_ESCROW_PROGRAM_PROGRAM_ADDRESS,
>(
  input: AllowMintInput<
    TAccountPayer,
    TAccountAdmin,
    TAccountInstance,
    TAccountMint,
    TAccountAllowedMint,
    TAccountInstanceAta,
    TAccountSystemProgram,
    TAccountTokenProgram,
    TAccountAssociatedTokenProgram,
    TAccountEventAuthority,
    TAccountPrivateChannelEscrowProgram
  >,
  config?: { programAddress?: TProgramAddress },
): AllowMintInstruction<
  TProgramAddress,
  TAccountPayer,
  TAccountAdmin,
  TAccountInstance,
  TAccountMint,
  TAccountAllowedMint,
  TAccountInstanceAta,
  TAccountSystemProgram,
  TAccountTokenProgram,
  TAccountAssociatedTokenProgram,
  TAccountEventAuthority,
  TAccountPrivateChannelEscrowProgram
> {
  // Program address.
  const programAddress =
    config?.programAddress ?? PRIVATE_CHANNEL_ESCROW_PROGRAM_PROGRAM_ADDRESS;

  // Original accounts.
  const originalAccounts = {
    payer: { value: input.payer ?? null, isWritable: true },
    admin: { value: input.admin ?? null, isWritable: false },
    instance: { value: input.instance ?? null, isWritable: false },
    mint: { value: input.mint ?? null, isWritable: false },
    allowedMint: { value: input.allowedMint ?? null, isWritable: true },
    instanceAta: { value: input.instanceAta ?? null, isWritable: true },
    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
    tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
    associatedTokenProgram: {
      value: input.associatedTokenProgram ?? null,
      isWritable: false,
    },
    eventAuthority: { value: input.eventAuthority ?? null, isWritable: false },
    privateChannelEscrowProgram: {
      value: input.privateChannelEscrowProgram ?? null,
      isWritable: false,
    },
  };
  const accounts = originalAccounts as Record<
    keyof typeof originalAccounts,
    ResolvedInstructionAccount
  >;

  // 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">;
  }
  if (!accounts.associatedTokenProgram.value) {
    accounts.associatedTokenProgram.value =
      "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" as Address<"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL">;
  }
  if (!accounts.privateChannelEscrowProgram.value) {
    accounts.privateChannelEscrowProgram.value =
      "9tgHa1DcnaSSUtmMsst8ovKTe1Gfxzezn27KnH9xXYeU" as Address<"9tgHa1DcnaSSUtmMsst8ovKTe1Gfxzezn27KnH9xXYeU">;
  }
  if (!args.bump) {
    args.bump = getResolvedInstructionAccountAsProgramDerivedAddress(
      "allowedMint",
      accounts.allowedMint.value,
    )[1];
  }

  const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
  return Object.freeze({
    accounts: [
      getAccountMeta("payer", accounts.payer),
      getAccountMeta("admin", accounts.admin),
      getAccountMeta("instance", accounts.instance),
      getAccountMeta("mint", accounts.mint),
      getAccountMeta("allowedMint", accounts.allowedMint),
      getAccountMeta("instanceAta", accounts.instanceAta),
      getAccountMeta("systemProgram", accounts.systemProgram),
      getAccountMeta("tokenProgram", accounts.tokenProgram),
      getAccountMeta("associatedTokenProgram", accounts.associatedTokenProgram),
      getAccountMeta("eventAuthority", accounts.eventAuthority),
      getAccountMeta(
        "privateChannelEscrowProgram",
        accounts.privateChannelEscrowProgram,
      ),
    ],
    data: getAllowMintInstructionDataEncoder().encode(
      args as AllowMintInstructionDataArgs,
    ),
    programAddress,
  } as AllowMintInstruction<
    TProgramAddress,
    TAccountPayer,
    TAccountAdmin,
    TAccountInstance,
    TAccountMint,
    TAccountAllowedMint,
    TAccountInstanceAta,
    TAccountSystemProgram,
    TAccountTokenProgram,
    TAccountAssociatedTokenProgram,
    TAccountEventAuthority,
    TAccountPrivateChannelEscrowProgram
  >);
}

export type ParsedAllowMintInstruction<
  TProgram extends string =
    typeof PRIVATE_CHANNEL_ESCROW_PROGRAM_PROGRAM_ADDRESS,
  TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[],
> = {
  programAddress: Address<TProgram>;
  accounts: {
    /** Transaction fee payer */
    payer: TAccountMetas[0];
    /** Admin of Instance */
    admin: TAccountMetas[1];
    /** Instance PDA to validate admin authority */
    instance: TAccountMetas[2];
    /** Token mint to be allowed */
    mint: TAccountMetas[3];
    /** PDA of the Allowed Mint */
    allowedMint: TAccountMetas[4];
    /** Instance Escrow account for specified mint */
    instanceAta: TAccountMetas[5];
    /** System program */
    systemProgram: TAccountMetas[6];
    /** Token program */
    tokenProgram: TAccountMetas[7];
    /** Associated Token program */
    associatedTokenProgram: TAccountMetas[8];
    /** Event authority PDA for emitting events */
    eventAuthority: TAccountMetas[9];
    /** Current program for CPI */
    privateChannelEscrowProgram: TAccountMetas[10];
  };
  data: AllowMintInstructionData;
};

export function parseAllowMintInstruction<
  TProgram extends string,
  TAccountMetas extends readonly AccountMeta[],
>(
  instruction: Instruction<TProgram> &
    InstructionWithAccounts<TAccountMetas> &
    InstructionWithData<ReadonlyUint8Array>,
): ParsedAllowMintInstruction<TProgram, TAccountMetas> {
  if (instruction.accounts.length < 11) {
    throw new SolanaError(
      SOLANA_ERROR__PROGRAM_CLIENTS__INSUFFICIENT_ACCOUNT_METAS,
      {
        actualAccountMetas: instruction.accounts.length,
        expectedAccountMetas: 11,
      },
    );
  }
  let accountIndex = 0;
  const getNextAccount = () => {
    const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!;
    accountIndex += 1;
    return accountMeta;
  };
  return {
    programAddress: instruction.programAddress,
    accounts: {
      payer: getNextAccount(),
      admin: getNextAccount(),
      instance: getNextAccount(),
      mint: getNextAccount(),
      allowedMint: getNextAccount(),
      instanceAta: getNextAccount(),
      systemProgram: getNextAccount(),
      tokenProgram: getNextAccount(),
      associatedTokenProgram: getNextAccount(),
      eventAuthority: getNextAccount(),
      privateChannelEscrowProgram: getNextAccount(),
    },
    data: getAllowMintInstructionDataDecoder().decode(instruction.data),
  };
}
