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

import { Chain, Stage } from '@layerzerolabs/lz-definitions'

import { ModuleManager } from './module-manager'
import {
    BlockedMessageLib,
    BlockedMessageLibPtbBuilder,
    Call,
    Counter,
    DVN,
    DVNFeeLib,
    DvnLayerZero,
    DvnPtbBuilder,
    Endpoint,
    EndpointPtbBuilder,
    Executor,
    ExecutorFeeLib,
    ExecutorLayerZero,
    ExecutorPtbBuilder,
    LayerZeroViews,
    OApp,
    PackageWhitelistValidator,
    PriceFeed,
    PtbBuilder,
    SimpleMessageLib,
    SimpleMessageLibPtbBuilder,
    Treasury,
    Uln302,
    Uln302PtbBuilder,
    Utils,
    WorkerRegistry,
    Zro,
} from './modules'
import { ResourceManager } from './resource'
import { ModuleOptions, ObjectOptions, PackageOptions, SdkOptions } from './types'

export * from './generated/addresses'

/**
 * The SDK class provides methods to interact with LayerZero on Sui.
 * Uses ModuleManager for centralized module management.
 */
export class SDK {
    public chain: Chain
    public stage: Stage
    public client: SuiClient
    public packages: PackageOptions
    public objects: ObjectOptions
    private moduleManager: ModuleManager

    /**
     * Creates an instance of the SDK.
     */
    constructor(options: SdkOptions) {
        this.chain = options.chain ?? Chain.SUI
        this.stage = options.stage ?? Stage.MAINNET
        this.client = options.client

        this.packages = ResourceManager.mergeDefaultPackageConfig(this.stage, options.packages)
        this.objects = ResourceManager.mergeDefaultObjectConfig(this.stage, options.objects)

        // Initialize all standard modules
        this.moduleManager = new ModuleManager(this.packages, this.objects)
        this.moduleManager.initializeCoreModules(this.packages, this.objects, this.client)
    }

    // === Core Module Getters (from ModuleManager) ===

    getEndpoint(): Endpoint {
        return this.moduleManager.getEndpoint()
    }

    getSimpleMessageLib(): SimpleMessageLib {
        return this.moduleManager.getSimpleMessageLib()
    }

    getBlockedMessageLib(): BlockedMessageLib {
        return this.moduleManager.getBlockedMessageLib()
    }

    getUln302(): Uln302 {
        return this.moduleManager.getUln302()
    }

    getUtils(): Utils {
        return this.moduleManager.getUtils()
    }

    getZro(): Zro {
        return this.moduleManager.getZro()
    }

    getCall(): Call {
        return this.moduleManager.getCall()
    }

    getPtbBuilder(): PtbBuilder {
        return this.moduleManager.getPtbBuilder()
    }

    getTreasury(): Treasury {
        return this.moduleManager.getTreasury()
    }

    getLayerZeroViews(): LayerZeroViews {
        return this.moduleManager.getLayerZeroViews()
    }

    getOApp(callCapId: string, options?: ModuleOptions): OApp {
        return this.moduleManager.getOApp(this.client, callCapId, options)
    }

    // === Non-Core Module Getters (created on-demand with caching) ===

    getCounter(options?: ModuleOptions): Counter {
        return this.moduleManager.getCounter(this.client, options)
    }

    getExecutor(options?: ModuleOptions): Executor {
        return this.moduleManager.getExecutor(this.client, options)
    }

    getDvn(options?: ModuleOptions): DVN {
        return this.moduleManager.getDvn(this.client, options)
    }

    getDvnFeeLib(options?: ModuleOptions): DVNFeeLib {
        return this.moduleManager.getDvnFeeLib(this.client, options)
    }

    getExecutorFeeLib(options?: ModuleOptions): ExecutorFeeLib {
        return this.moduleManager.getExecutorFeeLib(this.client, options)
    }

    getPriceFeed(options?: ModuleOptions): PriceFeed {
        return this.moduleManager.getPriceFeed(this.client, options)
    }

    getDvnLayerZero(options?: ModuleOptions): DvnLayerZero {
        return this.moduleManager.getDvnLayerZero(this.client, options)
    }

    getExecutorLayerZero(options?: ModuleOptions): ExecutorLayerZero {
        return this.moduleManager.getExecutorLayerZero(this.client, options)
    }

    getDvnPtbBuilder(options?: ModuleOptions): DvnPtbBuilder {
        return this.moduleManager.getDvnPtbBuilder(this.client, options)
    }

    getExecutorPtbBuilder(options?: ModuleOptions): ExecutorPtbBuilder {
        return this.moduleManager.getExecutorPtbBuilder(this.client, options)
    }

    getPackageWhitelistValidator(options?: ModuleOptions): PackageWhitelistValidator {
        return this.moduleManager.getPackageWhitelistValidator(this.client, options)
    }

    getUln302PtbBuilder(options?: ModuleOptions): Uln302PtbBuilder {
        return this.moduleManager.getUln302PtbBuilder(this.client, options)
    }

    getEndpointPtbBuilder(options?: ModuleOptions): EndpointPtbBuilder {
        return this.moduleManager.getEndpointPtbBuilder(this.client, options)
    }

    getSimpleMessageLibPtbBuilder(options?: ModuleOptions): SimpleMessageLibPtbBuilder {
        return this.moduleManager.getSimpleMessageLibPtbBuilder(this.client, options)
    }

    getBlockedMessageLibPtbBuilder(options?: ModuleOptions): BlockedMessageLibPtbBuilder {
        return this.moduleManager.getBlockedMessageLibPtbBuilder(this.client, options)
    }

    getWorkerRegistry(options?: ModuleOptions): WorkerRegistry {
        return this.moduleManager.getWorkerRegistry(this.client, options)
    }

    getOrCreateModule<T>(moduleName: string, options: ModuleOptions | undefined, factory: () => T): T {
        return this.moduleManager.getOrCreateModule(moduleName, options, factory)
    }
}
