import { SOLANA_ERROR__INSTRUCTION_ERROR__CUSTOM, SOLANA_ERROR__INSTRUCTION_ERROR__UNKNOWN } from './codes';
import { SolanaError } from './error';
import { getSolanaErrorFromRpcError } from './rpc-enum-errors';

const ORDERED_ERROR_NAMES = [
    // Keep synced with RPC source: https://github.com/anza-xyz/solana-sdk/blob/master/instruction-error/src/lib.rs
    // If this list ever gets too large, consider implementing a compression strategy like this:
    // https://gist.github.com/steveluscher/aaa7cbbb5433b1197983908a40860c47
    'GenericError',
    'InvalidArgument',
    'InvalidInstructionData',
    'InvalidAccountData',
    'AccountDataTooSmall',
    'InsufficientFunds',
    'IncorrectProgramId',
    'MissingRequiredSignature',
    'AccountAlreadyInitialized',
    'UninitializedAccount',
    'UnbalancedInstruction',
    'ModifiedProgramId',
    'ExternalAccountLamportSpend',
    'ExternalAccountDataModified',
    'ReadonlyLamportChange',
    'ReadonlyDataModified',
    'DuplicateAccountIndex',
    'ExecutableModified',
    'RentEpochModified',
    'NotEnoughAccountKeys',
    'AccountDataSizeChanged',
    'AccountNotExecutable',
    'AccountBorrowFailed',
    'AccountBorrowOutstanding',
    'DuplicateAccountOutOfSync',
    'Custom',
    'InvalidError',
    'ExecutableDataModified',
    'ExecutableLamportChange',
    'ExecutableAccountNotRentExempt',
    'UnsupportedProgramId',
    'CallDepth',
    'MissingAccount',
    'ReentrancyNotAllowed',
    'MaxSeedLengthExceeded',
    'InvalidSeeds',
    'InvalidRealloc',
    'ComputationalBudgetExceeded',
    'PrivilegeEscalation',
    'ProgramEnvironmentSetupFailure',
    'ProgramFailedToComplete',
    'ProgramFailedToCompile',
    'Immutable',
    'IncorrectAuthority',
    'BorshIoError',
    'AccountNotRentExempt',
    'InvalidAccountOwner',
    'ArithmeticOverflow',
    'UnsupportedSysvar',
    'IllegalOwner',
    'MaxAccountsDataAllocationsExceeded',
    'MaxAccountsExceeded',
    'MaxInstructionTraceLengthExceeded',
    'BuiltinProgramsMustConsumeComputeUnits',
];

export function getSolanaErrorFromInstructionError(
    /**
     * The index of the instruction inside the transaction.
     */
    index: bigint | number,
    instructionError: string | { [key: string]: unknown },
): SolanaError {
    const numberIndex = Number(index);
    return getSolanaErrorFromRpcError(
        {
            errorCodeBaseOffset: 4615001,
            getErrorContext(errorCode, rpcErrorName, rpcErrorContext) {
                if (errorCode === SOLANA_ERROR__INSTRUCTION_ERROR__UNKNOWN) {
                    return {
                        errorName: rpcErrorName,
                        index: numberIndex,
                        ...(rpcErrorContext !== undefined ? { instructionErrorContext: rpcErrorContext } : null),
                    };
                } else if (errorCode === SOLANA_ERROR__INSTRUCTION_ERROR__CUSTOM) {
                    return {
                        code: Number(rpcErrorContext as bigint | number),
                        index: numberIndex,
                    };
                }
                return { index: numberIndex };
            },
            orderedErrorNames: ORDERED_ERROR_NAMES,
            rpcEnumError: instructionError,
        },
        getSolanaErrorFromInstructionError,
    );
}
