import { bcs } from '@mysten/sui/bcs'
import { SuiClient } from '@mysten/sui/client'

import { ModuleManager } from '../module-manager'
import {
    EndpointExecutionState,
    EndpointExecutionStateType,
    ExecutableParams,
    InitializableParams,
    ObjectOptions,
    UlnVerifiableParams,
    UlnVerificationState,
    UlnVerificationStateType,
    VerifiableParams,
} from '../types'
import { asBytes, asObject, asU32, asU64, executeSimulate } from '../utils'

const MODULE_NAME_ENDPOINT_VIEWS = 'endpoint_views'
const MODULE_NAME_ULN_302_VIEWS = 'uln_302_views'

/**
 * LayerZero Views SDK
 *
 * Provides view functions for checking the state of LayerZero messages
 * and their executability/verification status without modifying state.
 */
export class LayerZeroViews {
    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
    }

    // ======== Endpoint View Functions ========

    /**
     * Checks if a message path is initializable (channel exists).
     *
     * @param params - Parameters containing messaging channel, source EID, and sender
     * @returns Promise<boolean> - True if the path is initializable
     */
    async initializable(params: InitializableParams): Promise<boolean> {
        return executeSimulate(
            this.client,
            (tx) => {
                tx.moveCall({
                    target: this.#endpointViewsTarget('initializable'),
                    arguments: [
                        asObject(tx, params.messagingChannel),
                        asU32(tx, params.srcEid),
                        asBytes(tx, params.sender),
                    ],
                })
            },
            (result) => bcs.Bool.parse(result[0].value)
        )
    }

    /**
     * Checks if a message is verifiable by the endpoint.
     *
     * @param params - Parameters containing messaging channel, source EID, sender, and nonce
     * @returns Promise<boolean> - True if the message is verifiable
     */
    async verifiable(params: VerifiableParams): Promise<boolean> {
        return executeSimulate(
            this.client,
            (tx) => {
                tx.moveCall({
                    target: this.#endpointViewsTarget('verifiable'),
                    arguments: [
                        asObject(tx, params.messagingChannel),
                        asU32(tx, params.srcEid),
                        asBytes(tx, params.sender),
                        asU64(tx, params.nonce),
                    ],
                })
            },
            (result) => bcs.Bool.parse(result[0].value)
        )
    }

    /**
     * Determines the execution state of a message.
     *
     * @param params - Parameters containing messaging channel, source EID, sender, and nonce
     * @returns Promise<EndpointExecutionStateType> - The current execution state
     */
    async executable(params: ExecutableParams): Promise<EndpointExecutionStateType> {
        return executeSimulate(
            this.client,
            (tx) => {
                tx.moveCall({
                    target: this.#endpointViewsTarget('executable'),
                    arguments: [
                        asObject(tx, params.messagingChannel),
                        asU32(tx, params.srcEid),
                        asBytes(tx, params.sender),
                        asU64(tx, params.nonce),
                    ],
                })
            },
            (result) => bcs.U8.parse(result[0].value) as EndpointExecutionStateType
        )
    }

    // ======== ULN 302 View Functions ========

    /**
     * Checks the verification state of a message through the ULN 302.
     *
     * @param params - Parameters containing ULN, verification, messaging channel, packet header, and payload hash
     * @returns Promise<UlnVerificationStateType> - The current verification state
     */
    async ulnVerifiable(params: UlnVerifiableParams): Promise<UlnVerificationStateType> {
        return executeSimulate(
            this.client,
            (tx) => {
                tx.moveCall({
                    target: this.#uln302ViewsTarget('verifiable'),
                    arguments: [
                        tx.object(this.objects.uln302),
                        asObject(tx, params.verification),
                        tx.object(this.objects.endpointV2),
                        asObject(tx, params.messagingChannel),
                        asBytes(tx, params.packetHeaderBytes),
                        asBytes(tx, params.payloadHash),
                    ],
                })
            },
            (result) => bcs.U8.parse(result[0].value) as UlnVerificationStateType
        )
    }

    // ======== Utility Functions ========

    /**
     * Gets the human-readable name for an endpoint execution state
     * @param state - The endpoint execution state enum value
     * @returns Human-readable string representation of the execution state
     */
    static getExecutionStateName(state: EndpointExecutionStateType): string {
        switch (state) {
            case EndpointExecutionState.STATE_NOT_EXECUTABLE:
                return 'NOT_EXECUTABLE'
            case EndpointExecutionState.STATE_VERIFIED_BUT_NOT_EXECUTABLE:
                return 'VERIFIED_BUT_NOT_EXECUTABLE'
            case EndpointExecutionState.STATE_EXECUTABLE:
                return 'EXECUTABLE'
            case EndpointExecutionState.STATE_EXECUTED:
                return 'EXECUTED'
            default:
                return 'UNKNOWN'
        }
    }

    /**
     * Gets the human-readable name for a ULN verification state
     * @param state - The ULN verification state enum value
     * @returns Human-readable string representation of the verification state
     */
    static getVerificationStateName(state: UlnVerificationStateType): string {
        switch (state) {
            case UlnVerificationState.STATE_VERIFYING:
                return 'VERIFYING'
            case UlnVerificationState.STATE_VERIFIABLE:
                return 'VERIFIABLE'
            case UlnVerificationState.STATE_VERIFIED:
                return 'VERIFIED'
            case UlnVerificationState.STATE_NOT_INITIALIZABLE:
                return 'NOT_INITIALIZABLE'
            default:
                return 'UNKNOWN'
        }
    }

    // ======== Private Helper Methods ========

    /**
     * Generate the full target path for endpoint view functions
     * @param functionName - The function name to call
     * @returns The full module path for the move call
     */
    #endpointViewsTarget(functionName: string): string {
        return `${this.packageId}::${MODULE_NAME_ENDPOINT_VIEWS}::${functionName}`
    }

    /**
     * Generate the full target path for ULN302 view functions
     * @param functionName - The function name to call
     * @returns The full module path for the move call
     */
    #uln302ViewsTarget(functionName: string): string {
        return `${this.packageId}::${MODULE_NAME_ULN_302_VIEWS}::${functionName}`
    }
}
