import { getAccount, watchAccount, type GetAccountReturnType } from '@wagmi/core'
import type { AppKit } from '@reown/appkit/vue'
import { getWagmiConfig, reownAppKit } from '@/config/reownAppKit'

const WALLET_CONNECT_TIMEOUT_MS = 45_000

let activeEvmWaitAbort: (() => void) | null = null
let activeTronWaitAbort: (() => void) | null = null

export class WalletFlowCancelledError extends Error {
  constructor(message = 'modal closed') {
    super(message)
    this.name = 'WalletFlowCancelledError'
  }
}

export function abortActiveEvmConnectWait() {
  activeEvmWaitAbort?.()
  activeEvmWaitAbort = null
}

export function abortActiveTronConnectWait() {
  activeTronWaitAbort?.()
  activeTronWaitAbort = null
}

export function abortAllReownConnectWaits() {
  abortActiveEvmConnectWait()
  abortActiveTronConnectWait()
}

function isModalOpen(appKit: AppKit): boolean {
  return Boolean(appKit.getState().open)
}

/** 弹窗关闭：已连接则继续；未连接则视为取消。 */
export function waitForEvmConnectAfterModalOpen(): Promise<void> {
  const appKit = reownAppKit
  if (!appKit) {
    return Promise.reject(new Error('未配置 VITE_WALLETCONNECT_PROJECT_ID'))
  }

  abortActiveEvmConnectWait()
  const config = getWagmiConfig()

  return new Promise((resolve, reject) => {
    let settled = false

    const finish = (action: 'ok' | 'cancel', error?: Error) => {
      if (settled) {
        return
      }
      settled = true
      cleanup()
      activeEvmWaitAbort = null
      if (action === 'ok') {
        resolve()
      } else {
        reject(error ?? new WalletFlowCancelledError())
      }
    }

    const cleanup = () => {
      unsubState?.()
      unwatch?.()
      window.clearTimeout(timer)
    }

    activeEvmWaitAbort = () => finish('cancel', new WalletFlowCancelledError())

    const timer = window.setTimeout(
      () => finish('cancel', new Error('钱包连接超时，请重新点击连接钱包')),
      WALLET_CONNECT_TIMEOUT_MS,
    )

    const hasAddress = () => Boolean(getAccount(config).address)

    const unsubState = appKit.subscribeState((state) => {
      if (settled || state.open) {
        return
      }
      if (hasAddress()) {
        finish('ok')
      } else {
        finish('cancel', new WalletFlowCancelledError())
      }
    })

    const unwatch = watchAccount(config, {
      onChange(account: GetAccountReturnType) {
        if (settled) {
          return
        }
        if (account.isConnected && account.address && !isModalOpen(appKit)) {
          finish('ok')
        }
      },
    })
  })
}

export function waitForTronConnectAfterModalOpen(): Promise<string> {
  const appKit = reownAppKit
  if (!appKit) {
    return Promise.reject(new Error('未配置 VITE_WALLETCONNECT_PROJECT_ID'))
  }

  abortActiveTronConnectWait()

  return new Promise((resolve, reject) => {
    let settled = false

    const finish = (action: 'ok' | 'cancel', address?: string, error?: Error) => {
      if (settled) {
        return
      }
      settled = true
      cleanup()
      activeTronWaitAbort = null
      if (action === 'ok' && address) {
        resolve(address)
      } else {
        reject(error ?? new WalletFlowCancelledError())
      }
    }

    const cleanup = () => {
      unsubState?.()
      unsubAccount?.()
      window.clearTimeout(timer)
    }

    activeTronWaitAbort = () => finish('cancel', undefined, new WalletFlowCancelledError())

    const timer = window.setTimeout(
      () => finish('cancel', undefined, new Error('钱包连接超时，请重新点击连接钱包')),
      WALLET_CONNECT_TIMEOUT_MS,
    )

    const currentAddress = () => appKit.getAccount('tron')?.address?.trim() || null

    const unsubState = appKit.subscribeState((state) => {
      if (settled || state.open) {
        return
      }
      const addr = currentAddress()
      if (addr) {
        finish('ok', addr)
      } else {
        finish('cancel', undefined, new WalletFlowCancelledError())
      }
    })

    const unsubAccount = appKit.subscribeAccount((state) => {
      if (settled || !state.isConnected || !state.address) {
        return
      }
      const addr = currentAddress()
      if (addr && !isModalOpen(appKit)) {
        finish('ok', addr)
      }
    }, 'tron')
  })
}
