import { getWalletConnectConfig, type WalletConnectConfigResponse } from '@/services/api/walletConnect'

export interface WalletConnectSessionResult {
  address: string
  chainId: number
}

let activeProvider: Awaited<ReturnType<typeof createEthereumProvider>> | null = null

async function createEthereumProvider(projectId: string, chains: number[]) {
  const { default: EthereumProvider } = await import('@walletconnect/ethereum-provider')
  const resolvedChains = (chains.length ? chains : [138, 1]) as [number, ...number[]]
  return EthereumProvider.init({
    projectId,
    chains: [resolvedChains[0]],
    optionalChains: resolvedChains,
    showQrModal: true,
    metadata: {
      name: 'DBIS Explorer',
      description: 'Chain 138 explorer by DBIS',
      url: typeof window !== 'undefined' ? window.location.origin : 'https://explorer.d-bis.org',
      icons: [`${typeof window !== 'undefined' ? window.location.origin : 'https://explorer.d-bis.org'}/favicon.ico`],
    },
  })
}

export async function loadWalletConnectConfig(): Promise<WalletConnectConfigResponse | null> {
  return getWalletConnectConfig()
}

export function isWalletConnectClientReady(config: WalletConnectConfigResponse | null): config is WalletConnectConfigResponse {
  return Boolean(config?.enabled && config.projectId)
}

export async function connectAndAuthenticateWalletConnect(
  config: WalletConnectConfigResponse,
  authenticate: (resolveAddress: () => Promise<string>, signMessage: (message: string, address: string) => Promise<string>) => Promise<{ address: string }>,
): Promise<{ address: string }> {
  if (!config.projectId) {
    throw new Error('WalletConnect project ID is not published.')
  }

  const chains = config.supportedChains?.length ? config.supportedChains : [138, 1]
  const provider = await createEthereumProvider(config.projectId, chains)
  activeProvider = provider

  return authenticate(
    async () => {
      const accounts = (await provider.enable()) as string[]
      const address = accounts?.[0]
      if (!address) {
        throw new Error('WalletConnect did not return an account.')
      }
      return address
    },
    async (message, address) => {
      return (await provider.request({
        method: 'personal_sign',
        params: [message, address],
      })) as string
    },
  )
}

export async function disconnectWalletConnect(): Promise<void> {
  if (!activeProvider) return
  try {
    await activeProvider.disconnect()
  } finally {
    activeProvider = null
  }
}

export function getActiveWalletConnectSessionId(): string | null {
  const session = activeProvider?.session
  return typeof session?.topic === 'string' && session.topic ? session.topic : null
}

/** Use for EIP-747 / EIP-3085 when no injected `window.ethereum` (e.g. desktop without extension). */
export function getActiveWalletConnectProvider(): Awaited<ReturnType<typeof createEthereumProvider>> | null {
  return activeProvider
}
