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

import { ModuleManager } from '../module-manager'
import { asU64 } from '../utils'

import type { CoinStruct } from '@mysten/sui/client'

export class Zro {
    public packageId: string
    public readonly client: SuiClient
    public zroType: string

    constructor(
        packageId: string,
        client: SuiClient,
        private readonly moduleManager: ModuleManager
    ) {
        this.packageId = packageId
        this.client = client
        this.zroType = `${this.packageId}::zro::ZRO`
    }

    // === Utils function ===

    /**
     * Split ZRO tokens and wrap in an Option type for use in LayerZero operations
     * If value is 0, returns Option::None, otherwise returns Option::Some with the split tokens
     * @param tx - The transaction to add the move call to
     * @param userAddress - Address of the user whose ZRO tokens to split
     * @param value - Amount of ZRO tokens to split (0 for None option)
     * @param maxCoins - Maximum total number of coins to collect across all pages (default: 200)
     * @param maxCoinsPerPage - Maximum number of coins to fetch per page (default: 50)
     * @returns Promise resolving to transaction result containing Option<Coin<ZRO>>
     * @throws Error if insufficient ZRO balance
     */
    async splitOptionZroTokenMoveCall(
        tx: Transaction,
        userAddress: string,
        value: bigint,
        maxCoins = 200,
        maxCoinsPerPage = 50
    ): Promise<TransactionResult> {
        if (value === 0n) {
            return this.noneOptionMoveCall(tx)
        }
        const zroCoin = await this.splitZroTokenMoveCall(tx, userAddress, value, maxCoins, maxCoinsPerPage)
        return this.someOptionMoveCall(tx, zroCoin)
    }

    /**
     * Splits specified amount of ZRO tokens from user's wallet
     * This method fetches coins and merges them as needed to get sufficient balance
     * @param tx - The transaction to add the move call to
     * @param owner - Address of the user whose ZRO coins to split
     * @param amount - Amount of ZRO tokens to split (in smallest units, considering 9 decimals)
     * @param limit - Maximum total number of coins to collect across all pages (default: 200)
     * @param pageSize - Maximum number of coins to fetch per page (default: 50)
     * @returns Promise resolving to split ZRO coin as TransactionResult
     * @throws Error if insufficient ZRO balance or no coins found
     */
    async splitZroTokenMoveCall(
        tx: Transaction,
        owner: string,
        amount: bigint,
        limit = 200,
        pageSize = 50
    ): Promise<TransactionResult> {
        return this.moduleManager.getUtils().splitCoinMoveCall(tx, this.zroType, owner, amount, limit, pageSize)
    }

    /**
     * Creates an Option::Some moveCall containing a ZRO coin
     * @param tx - The transaction to add the move call to
     * @param zroCoin - ZRO coin to wrap in Option or transaction argument
     * @returns Transaction result containing Option<Coin<ZRO>> type with Some value
     */
    someOptionMoveCall(tx: Transaction, zroCoin: TransactionArgument): TransactionResult {
        return tx.moveCall({
            target: `0x1::option::some`,
            typeArguments: [`0x2::coin::Coin<${this.zroType}>`],
            arguments: [zroCoin],
        })
    }

    /**
     * Creates an Option::None moveCall for ZRO coin type
     * @param tx - The transaction to add the move call to
     * @returns Transaction result containing Option<Coin<ZRO>> type with None value
     */
    noneOptionMoveCall(tx: Transaction): TransactionResult {
        return tx.moveCall({
            target: `0x1::option::none`,
            typeArguments: [`0x2::coin::Coin<${this.zroType}>`],
        })
    }
}
