import { bcs } from '@mysten/sui/bcs'
import { SuiClient } from '@mysten/sui/client'
import { Transaction, TransactionArgument, TransactionResult } from '@mysten/sui/transactions'

import { ExecutorConfigBcs, OAppUlnConfigBcs, UlnConfigBcs } from '../../bcs'
import { ModuleManager } from '../../module-manager'
import { ExecutorConfig, OAppUlnConfig, ObjectOptions, UlnConfig, VersionInfo } from '../../types'
import {
    asAddress,
    asAddressVector,
    asBytes,
    asBytes32,
    asObject,
    asU32,
    asU64,
    asU8,
    executeSimulate,
} from '../../utils'

const MODULE_NAME = 'uln_302'

// ULN-302 specific error codes (matching uln-302 contract files)
export const Uln302ErrorCode = {
    SendUln302_EDefaultExecutorConfigNotFound: 1,
    SendUln302_EDefaultUlnConfigNotFound: 2,
    SendUln302_EInvalidMessageSize: 3,
    SendUln302_EOAppExecutorConfigNotFound: 4,
    SendUln302_EOAppUlnConfigNotFound: 5,

    ReceiveUln302_EConfirmationsNotFound: 1,
    ReceiveUln302_EDefaultUlnConfigNotFound: 2,
    ReceiveUln302_EInvalidEid: 3,
    ReceiveUln302_EOAppUlnConfigNotFound: 4,
    ReceiveUln302_EVerifying: 5,

    Uln302_EInvalidConfigType: 1,
    Uln302_EInvalidMessagingChannel: 2,
    Uln302_EUnsupportedEid: 3,

    // UlnConfig related errors (matching uln_config.move)
    UlnConfig_EAtLeastOneDVN: 1,
    UlnConfig_EDuplicateOptionalDVNs: 2,
    UlnConfig_EDuplicateRequiredDVNs: 3,
    UlnConfig_EInvalidOptionalDVNCount: 4,
    UlnConfig_EInvalidOptionalDVNThreshold: 5,
    UlnConfig_EInvalidRequiredDVNCount: 6,
    UlnConfig_EInvalidUlnConfigBytes: 7,

    // OAppUlnConfig related errors (matching oapp_uln_config.move)
    OAppUlnConfig_EInvalidConfirmations: 1,
    OAppUlnConfig_EInvalidRequiredDVNs: 2,
    OAppUlnConfig_EInvalidOptionalDVNs: 3,

    // ExecutorConfig related errors (matching executor_config.move)
    ExecutorConfig_EInvalidExecutorAddress: 1,
    ExecutorConfig_EInvalidExecutorBytes: 2,
    ExecutorConfig_EZeroMessageSize: 3,
} as const

export class Uln302 {
    public packageId: string
    public readonly client: SuiClient
    private readonly objects: ObjectOptions

    constructor(
        packageId: string,
        client: SuiClient,
        objects: ObjectOptions,
        private readonly moduleManager: ModuleManager
    ) {
        this.packageId = packageId
        this.client = client
        this.objects = objects
    }

    // === Set Functions ===

    /**
     * Create quote move call for message library
     * Note: This is typically called by the endpoint, not directly by users
     * @param tx - The transaction to add the move call to
     * @param call - The call transaction result containing quote parameters
     * @returns Transaction result containing the quote operation
     */
    quoteMoveCall(tx: Transaction, call: TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('quote'),
            arguments: [tx.object(this.objects.uln302), call],
        })
    }

    /**
     * Confirm quote move call with worker fee calculations
     * Note: This is typically called by the endpoint, not directly by users
     * @param tx - The transaction to add the move call to
     * @param treasury - Treasury object address
     * @param messageLibCall - Message library call transaction result
     * @param executorCall - Executor call transaction result
     * @param dvnMultiCall - DVN multi-call transaction result
     */
    confirmQuoteMoveCall(
        tx: Transaction,
        treasury: string | TransactionArgument,
        messageLibCall: TransactionArgument,
        executorCall: TransactionArgument,
        dvnMultiCall: TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('confirm_quote'),
            arguments: [
                tx.object(this.objects.uln302),
                asObject(tx, treasury),
                messageLibCall,
                executorCall,
                dvnMultiCall,
            ],
        })
    }

    /**
     * Create send move call for message library
     * Note: This is typically called by the endpoint, not directly by users
     * @param tx - The transaction to add the move call to
     * @param call - The call transaction result containing send parameters
     * @returns Transaction result containing the send operation
     */
    sendMoveCall(tx: Transaction, call: TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('send'),
            arguments: [tx.object(this.objects.uln302), call],
        })
    }

    /**
     * Confirm send move call with treasury and worker integration
     * Note: This is typically called by the endpoint, not directly by users
     * @param tx - The transaction to add the move call to
     * @param treasury - Treasury object address
     * @param messagingChannel - The messaging channel object ID
     * @param endpointCall - The endpoint call transaction result
     * @param messageLibCall - The message library call transaction result
     * @param executorCall - The executor call transaction result
     * @param dvnMultiCall - The DVN multi-call transaction result
     */
    confirmSendMoveCall(
        tx: Transaction,
        treasury: string | TransactionArgument,
        messagingChannel: string | TransactionArgument,
        endpointCall: TransactionArgument,
        messageLibCall: TransactionArgument,
        executorCall: TransactionArgument,
        dvnMultiCall: TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('confirm_send'),
            arguments: [
                tx.object(this.objects.uln302),
                tx.object(this.objects.endpointV2),
                asObject(tx, treasury),
                asObject(tx, messagingChannel),
                endpointCall,
                messageLibCall,
                executorCall,
                dvnMultiCall,
            ],
        })
    }

    /**
     * Set configuration move call for ULN302
     * Note: This is typically called by the endpoint, not directly by users
     * @param tx - The transaction to add the move call to
     * @param call - The call transaction result containing configuration
     */
    setConfigMoveCall(tx: Transaction, call: TransactionArgument): void {
        tx.moveCall({
            target: this.#target('set_config'),
            arguments: [tx.object(this.objects.uln302), call],
        })
    }

    /**
     * Verify packet move call with DVN call (new API)
     * Note: This is typically called by DVNs via Call objects, not directly by users
     * @param tx - The transaction to add the move call to
     * @param verification - The verification object address or transaction argument
     * @param call - The DVN verification call containing verification parameters
     */
    verifyMoveCall(tx: Transaction, verification: string | TransactionArgument, call: TransactionArgument): void {
        tx.moveCall({
            target: this.#target('verify'),
            arguments: [tx.object(this.objects.uln302), asObject(tx, verification), call],
        })
    }

    /**
     * Commit verification move call after all DVN verifications are complete
     * Note: This is typically called by relayers/executors, not directly by users
     * @param tx - The transaction to add the move call to
     * @param verification - The verification object address
     * @param messagingChannel - The messaging channel object ID
     * @param packetHeader - The packet header as bytes
     * @param payloadHash - The payload hash as bytes
     */
    commitVerificationMoveCall(
        tx: Transaction,
        verification: string | TransactionArgument,
        messagingChannel: string | TransactionArgument,
        packetHeader: Uint8Array | TransactionArgument,
        payloadHash: Uint8Array | TransactionArgument
    ): void {
        tx.moveCall({
            target: this.#target('commit_verification'),
            arguments: [
                tx.object(this.objects.uln302),
                asObject(tx, verification),
                tx.object(this.objects.endpointV2),
                asObject(tx, messagingChannel),
                asBytes(tx, packetHeader),
                asBytes32(tx, payloadHash, this.moduleManager.getUtils()),
                tx.object.clock(),
            ],
        })
    }

    /**
     * Set default executor configuration for destination EID (admin only)
     * @param tx - The transaction to add the move call to
     * @param dstEid - Destination endpoint ID or transaction argument
     * @param config - Executor configuration parameters
     */
    setDefaultExecutorConfigMoveCall(
        tx: Transaction,
        dstEid: number | TransactionArgument,
        config: ExecutorConfig
    ): void {
        const executorConfig = this.createExecutorConfigMoveCall(tx, config)
        tx.moveCall({
            target: this.#target('set_default_executor_config'),
            arguments: [
                tx.object(this.objects.uln302),
                tx.object(this.objects.uln302AdminCap),
                asU32(tx, dstEid),
                executorConfig,
            ],
        })
    }

    /**
     * Set default send ULN configuration for destination EID (admin only)
     * @param tx - The transaction to add the move call to
     * @param dstEid - Destination endpoint ID or transaction argument
     * @param config - ULN configuration parameters
     */
    setDefaultSendUlnConfigMoveCall(tx: Transaction, dstEid: number | TransactionArgument, config: UlnConfig): void {
        const ulnConfig = this.createUlnConfigMoveCall(tx, config)
        tx.moveCall({
            target: this.#target('set_default_send_uln_config'),
            arguments: [
                tx.object(this.objects.uln302),
                tx.object(this.objects.uln302AdminCap),
                asU32(tx, dstEid),
                ulnConfig,
            ],
        })
    }

    /**
     * Set default receive ULN configuration for source EID (admin only)
     * @param tx - The transaction to add the move call to
     * @param srcEid - Source endpoint ID or transaction argument
     * @param config - ULN configuration parameters
     */
    setDefaultReceiveUlnConfigMoveCall(tx: Transaction, srcEid: number | TransactionArgument, config: UlnConfig): void {
        const ulnConfig = this.createUlnConfigMoveCall(tx, config)
        tx.moveCall({
            target: this.#target('set_default_receive_uln_config'),
            arguments: [
                tx.object(this.objects.uln302),
                tx.object(this.objects.uln302AdminCap),
                asU32(tx, srcEid),
                ulnConfig,
            ],
        })
    }

    // === View Functions ===

    /**
     * Get ULN-302 version information
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing version information
     */
    versionMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: this.#target('version'),
            arguments: [],
        })
    }

    /**
     * Get ULN-302 version information
     * @returns Promise<VersionInfo> - Version information with major, minor, and endpoint version
     */
    async version(): Promise<VersionInfo> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.versionMoveCall(tx)
            },
            (result) => ({
                major: BigInt(bcs.U64.parse(result[0].value)),
                minor: bcs.U8.parse(result[1].value),
                endpointVersion: bcs.U8.parse(result[2].value),
            })
        )
    }

    /**
     * Check if endpoint ID is supported by ULN-302
     * @param tx - The transaction to add the move call to
     * @param eid - Endpoint ID to check
     * @returns Transaction result containing support status
     */
    isSupportedEidMoveCall(tx: Transaction, eid: number | TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('is_supported_eid'),
            arguments: [tx.object(this.objects.uln302), asU32(tx, eid)],
        })
    }

    /**
     * Check if endpoint ID is supported by ULN-302
     * @param eid - Endpoint ID to check
     * @returns Promise<boolean> - True if the endpoint ID is supported
     */
    async isSupportedEid(eid: number): Promise<boolean> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.isSupportedEidMoveCall(tx, eid)
            },
            (result) => bcs.Bool.parse(result[0].value)
        )
    }

    /**
     * Get default executor configuration for destination EID
     * @param tx - The transaction to add the move call to
     * @param dstEid - Destination endpoint ID
     * @returns Transaction result containing the executor configuration
     */
    getDefaultExecutorConfigMoveCall(tx: Transaction, dstEid: number | TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_default_executor_config'),
            arguments: [tx.object(this.objects.uln302), asU32(tx, dstEid)],
        })
    }

    /**
     * Get default executor configuration for destination EID
     */
    async getDefaultExecutorConfig(dstEid: number): Promise<ExecutorConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getDefaultExecutorConfigMoveCall(tx, dstEid)
            },
            (result) => this.parseExecutorConfig(result[0].value)
        )
    }

    /**
     * Get OApp-specific executor configuration
     * @param tx - The transaction to add the move call to
     * @param sender - The sender OApp address
     * @param dstEid - Destination endpoint ID
     * @returns Transaction result containing the executor configuration
     */
    getOAppExecutorConfigMoveCall(
        tx: Transaction,
        sender: string | TransactionArgument,
        dstEid: number | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_oapp_executor_config'),
            arguments: [tx.object(this.objects.uln302), asAddress(tx, sender), asU32(tx, dstEid)],
        })
    }

    /**
     * Get OApp-specific executor configuration
     */
    async getOAppExecutorConfig(sender: string, dstEid: number): Promise<ExecutorConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getOAppExecutorConfigMoveCall(tx, sender, dstEid)
            },
            (result) => this.parseExecutorConfig(result[0].value)
        )
    }

    /**
     * Get effective executor configuration (OApp-specific or default)
     * @param tx - The transaction to add the move call to
     * @param sender - The sender OApp address
     * @param dstEid - Destination endpoint ID
     * @returns Transaction result containing the effective executor configuration
     */
    getEffectiveExecutorConfigMoveCall(
        tx: Transaction,
        sender: string | TransactionArgument,
        dstEid: number | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_effective_executor_config'),
            arguments: [tx.object(this.objects.uln302), asAddress(tx, sender), asU32(tx, dstEid)],
        })
    }

    /**
     * Get effective executor configuration (OApp-specific or default)
     */
    async getEffectiveExecutorConfig(sender: string, dstEid: number): Promise<ExecutorConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getEffectiveExecutorConfigMoveCall(tx, sender, dstEid)
            },
            (result) => this.parseExecutorConfig(result[0].value)
        )
    }

    /**
     * Get default send ULN configuration for destination EID
     * @param tx - The transaction to add the move call to
     * @param dstEid - Destination endpoint ID
     * @returns Transaction result containing the default send ULN configuration
     */
    getDefaultSendUlnConfigMoveCall(tx: Transaction, dstEid: number | TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_default_send_uln_config'),
            arguments: [tx.object(this.objects.uln302), asU32(tx, dstEid)],
        })
    }

    /**
     * Get default ULN send configuration
     */
    async getDefaultSendUlnConfig(dstEid: number): Promise<UlnConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getDefaultSendUlnConfigMoveCall(tx, dstEid)
            },
            (result) => this.parseUlnConfig(result[0].value)
        )
    }

    /**
     * Get OApp-specific send ULN configuration
     * @param tx - The transaction to add the move call to
     * @param sender - The sender OApp address
     * @param dstEid - Destination endpoint ID
     * @returns Transaction result containing the OApp send ULN configuration
     */
    getOAppSendUlnConfigMoveCall(
        tx: Transaction,
        sender: string | TransactionArgument,
        dstEid: number | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_oapp_send_uln_config'),
            arguments: [tx.object(this.objects.uln302), asAddress(tx, sender), asU32(tx, dstEid)],
        })
    }

    /**
     * Get OApp-specific ULN send configuration
     */
    async getOAppSendUlnConfig(sender: string, dstEid: number): Promise<OAppUlnConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getOAppSendUlnConfigMoveCall(tx, sender, dstEid)
            },
            (result) => this.parseOAppUlnConfig(result[0].value)
        )
    }

    /**
     * Get effective send ULN configuration (OApp-specific or default)
     * @param tx - The transaction to add the move call to
     * @param sender - The sender OApp address
     * @param dstEid - Destination endpoint ID
     * @returns Transaction result containing the effective send ULN configuration
     */
    getEffectiveSendUlnConfigMoveCall(
        tx: Transaction,
        sender: string | TransactionArgument,
        dstEid: number | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_effective_send_uln_config'),
            arguments: [tx.object(this.objects.uln302), asAddress(tx, sender), asU32(tx, dstEid)],
        })
    }

    /**
     * Get effective ULN send configuration
     */
    async getEffectiveSendUlnConfig(sender: string, dstEid: number): Promise<UlnConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getEffectiveSendUlnConfigMoveCall(tx, sender, dstEid)
            },
            (result) => this.parseUlnConfig(result[0].value)
        )
    }

    /**
     * Get default receive ULN configuration for source EID
     * @param tx - The transaction to add the move call to
     * @param srcEid - Source endpoint ID
     * @returns Transaction result containing the default receive ULN configuration
     */
    getDefaultReceiveUlnConfigMoveCall(tx: Transaction, srcEid: number | TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_default_receive_uln_config'),
            arguments: [tx.object(this.objects.uln302), asU32(tx, srcEid)],
        })
    }

    /**
     * Get default ULN receive configuration
     */
    async getDefaultReceiveUlnConfig(srcEid: number): Promise<UlnConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getDefaultReceiveUlnConfigMoveCall(tx, srcEid)
            },
            (result) => this.parseUlnConfig(result[0].value)
        )
    }

    /**
     * Get OApp-specific receive ULN configuration
     * @param tx - The transaction to add the move call to
     * @param receiver - The receiver OApp address
     * @param srcEid - Source endpoint ID
     * @returns Transaction result containing the OApp receive ULN configuration
     */
    getOAppReceiveUlnConfigMoveCall(
        tx: Transaction,
        receiver: string | TransactionArgument,
        srcEid: number | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_oapp_receive_uln_config'),
            arguments: [tx.object(this.objects.uln302), asAddress(tx, receiver), asU32(tx, srcEid)],
        })
    }

    /**
     * Get OApp-specific ULN receive configuration
     */
    async getOAppReceiveUlnConfig(receiver: string, srcEid: number): Promise<OAppUlnConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getOAppReceiveUlnConfigMoveCall(tx, receiver, srcEid)
            },
            (result) => this.parseOAppUlnConfig(result[0].value)
        )
    }

    /**
     * Get effective receive ULN configuration (OApp-specific or default)
     * @param tx - The transaction to add the move call to
     * @param receiver - The receiver OApp address
     * @param srcEid - Source endpoint ID
     * @returns Transaction result containing the effective receive ULN configuration
     */
    getEffectiveReceiveUlnConfigMoveCall(
        tx: Transaction,
        receiver: string | TransactionArgument,
        srcEid: number | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_effective_receive_uln_config'),
            arguments: [tx.object(this.objects.uln302), asAddress(tx, receiver), asU32(tx, srcEid)],
        })
    }

    /**
     * Get effective ULN receive configuration
     */
    async getEffectiveReceiveUlnConfig(receiver: string, srcEid: number): Promise<UlnConfig> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getEffectiveReceiveUlnConfigMoveCall(tx, receiver, srcEid)
            },
            (result) => this.parseUlnConfig(result[0].value)
        )
    }

    /**
     * Get verification object address for ULN-302
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing the verification object address
     */
    getVerificationMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_verification'),
            arguments: [tx.object(this.objects.uln302)],
        })
    }

    /**
     * Get verification object address for ULN-302
     * @returns Promise<string> - The verification object address
     */
    async getVerification(): Promise<string> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getVerificationMoveCall(tx)
            },
            (result) => bcs.Address.parse(result[0].value)
        )
    }

    /**
     * Check if packet is verifiable through ULN-302
     * @param tx - The transaction to add the move call to
     * @param verification - The verification object address
     * @param packetHeader - The packet header as bytes
     * @param payloadHash - The payload hash as bytes
     * @returns Transaction result containing the verifiable status
     */
    verifiableMoveCall(
        tx: Transaction,
        verification: string | TransactionArgument,
        packetHeader: Uint8Array | TransactionArgument,
        payloadHash: Uint8Array | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('verifiable'),
            arguments: [
                tx.object(this.objects.uln302),
                asObject(tx, verification),
                tx.object(this.objects.endpointV2),
                asBytes(tx, packetHeader),
                asBytes32(tx, payloadHash, this.moduleManager.getUtils()),
            ],
        })
    }

    /**
     * Check if packet is verifiable through ULN-302
     * @param verification - Verification object address
     * @param packetHeader - Packet header as bytes
     * @param payloadHash - Payload hash as bytes
     * @returns Promise<boolean> - True if packet is verifiable
     */
    async verifiable(verification: string, packetHeader: Uint8Array, payloadHash: Uint8Array): Promise<boolean> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.verifiableMoveCall(tx, verification, packetHeader, payloadHash)
            },
            (result) => bcs.Bool.parse(result[0].value)
        )
    }

    /**
     * Get confirmations for a specific DVN verification
     * @param tx - The transaction to add the move call to
     * @param verification - The verification object address
     * @param dvn - The DVN address
     * @param headerHash - The header hash as bytes
     * @param payloadHash - The payload hash as bytes
     * @returns Transaction result containing the confirmations count
     */
    getConfirmationsMoveCall(
        tx: Transaction,
        verification: string | TransactionArgument,
        dvn: string | TransactionArgument,
        headerHash: Uint8Array | TransactionArgument,
        payloadHash: Uint8Array | TransactionArgument
    ): TransactionResult {
        return tx.moveCall({
            target: this.#target('get_confirmations'),
            arguments: [
                asObject(tx, verification),
                asAddress(tx, dvn),
                asBytes32(tx, headerHash, this.moduleManager.getUtils()),
                asBytes32(tx, payloadHash, this.moduleManager.getUtils()),
            ],
        })
    }

    /**
     * Get confirmations for a specific DVN verification
     * @param verification - The verification object address
     * @param dvn - The DVN address
     * @param headerHash - The header hash as bytes
     * @param payloadHash - The payload hash as bytes
     * @returns Promise<bigint> - The confirmations count
     */
    async getConfirmations(
        verification: string,
        dvn: string,
        headerHash: Uint8Array,
        payloadHash: Uint8Array
    ): Promise<bigint> {
        return executeSimulate(
            this.client,
            (tx) => {
                this.getConfirmationsMoveCall(tx, verification, dvn, headerHash, payloadHash)
            },
            (result) => BigInt(bcs.U64.parse(result[0].value))
        )
    }

    // === Private Functions ===

    /**
     * Create executor configuration move call
     * @param tx - The transaction to add the move call to
     * @param config - Executor configuration parameters
     * @returns Transaction result containing the executor configuration
     * @private
     */
    private createExecutorConfigMoveCall(tx: Transaction, config: ExecutorConfig): TransactionResult {
        return tx.moveCall({
            target: this.#target('create', 'executor_config'),
            arguments: [asU64(tx, config.maxMessageSize), asAddress(tx, config.executor)],
        })
    }

    /**
     * Create ULN configuration move call
     * @param tx - The transaction to add the move call to
     * @param config - ULN configuration parameters
     * @returns Transaction result containing the ULN configuration
     * @private
     */
    private createUlnConfigMoveCall(tx: Transaction, config: UlnConfig): TransactionResult {
        return tx.moveCall({
            target: this.#target('create', 'uln_config'),
            arguments: [
                asU64(tx, config.confirmations),
                asAddressVector(tx, config.requiredDvns),
                asAddressVector(tx, config.optionalDvns),
                asU8(tx, config.optionalDvnThreshold),
            ],
        })
    }

    /**
     * Parse executor configuration from BCS data
     * @param data - Raw BCS data to parse
     * @returns ExecutorConfig - Parsed executor configuration
     * @private
     */
    private parseExecutorConfig(data: Uint8Array): ExecutorConfig {
        const config = ExecutorConfigBcs.parse(data)
        return {
            maxMessageSize: Number(config.max_message_size),
            executor: config.executor,
        }
    }

    /**
     * Parse ULN configuration from BCS data
     * @param data - Raw BCS data to parse
     * @returns UlnConfig - Parsed ULN configuration
     * @private
     */
    private parseUlnConfig(data: Uint8Array): UlnConfig {
        const config = UlnConfigBcs.parse(data)
        return {
            confirmations: BigInt(config.confirmations),
            requiredDvns: config.required_dvns,
            optionalDvns: config.optional_dvns,
            optionalDvnThreshold: config.optional_dvn_threshold,
        }
    }

    /**
     * Parse OApp ULN configuration from BCS data
     * @param data - Raw BCS data to parse
     * @returns OAppUlnConfig - Parsed OApp ULN configuration
     * @private
     */
    private parseOAppUlnConfig(data: Uint8Array): OAppUlnConfig {
        const config = OAppUlnConfigBcs.parse(data)
        const ulnConfig = config.uln_config
        return {
            useDefaultConfirmations: config.use_default_confirmations,
            useDefaultRequiredDvns: config.use_default_required_dvns,
            useDefaultOptionalDvns: config.use_default_optional_dvns,
            ulnConfig: {
                confirmations: BigInt(ulnConfig.confirmations),
                requiredDvns: ulnConfig.required_dvns,
                optionalDvns: ulnConfig.optional_dvns,
                optionalDvnThreshold: ulnConfig.optional_dvn_threshold,
            },
        }
    }

    /**
     * Generate the full target path for move calls
     * @param name - The function name to call
     * @param module_name - The module name (defaults to MODULE_NAME)
     * @returns The full module path for the move call
     * @private
     */
    #target(name: string, module_name: string = MODULE_NAME): string {
        return `${this.packageId}::${module_name}::${name}`
    }
}
