'use client'

import { useEffect, useMemo, useState } from 'react'
import DisclosureSection from '@/components/common/DisclosureSection'
import ListFilterBar from '@/components/common/ListFilterBar'
import { resolveExplorerApiBase } from '@/libs/frontend-api-client/api-base'
import type { TokenListToken, WalletChain } from '@/components/wallet/AddToMetaMask'
import { getActiveWalletConnectProvider } from '@/services/wallet/walletConnectClient'
import {
  WALLET_FEATURED_SYMBOLS_BY_CHAIN,
  WALLET_IMPORT_CHAIN_IDS,
  chainLabel,
} from '@/utils/walletChainCatalog'
import { toWalletAddEthereumChainParams } from '@/utils/walletAddEthereumChain'
import {
  isMobileWalletContext,
  MOBILE_WALLET_BUTTON_CLASS,
  resolveWalletEthereumProvider,
} from '@/utils/walletProviderEnv'
import { runWatchAssetBatch } from '@/utils/walletWatchAsset'
import { dedupeWalletWatchTokens } from '@/utils/walletWatchEligible'

type WatchAssetEntry = {
  type: 'ERC20'
  options: {
    address: string
    symbol: string
    decimals: number
    image?: string
  }
}

type MetaMaskChainPayload = {
  chainId?: number
  addEthereumChain?: WalletChain
  watchAssets?: WatchAssetEntry[]
}

type ChainImportState = {
  chainId: number
  network: WalletChain | null
  tokens: TokenListToken[]
  loading: boolean
  error: string | null
}

type PendingMultiChainFlow = {
  chainId: number
  tokens: TokenListToken[]
  label: string
  nextIndex: number
  totalAdded: number
}

function isTokenListToken(value: unknown): value is TokenListToken {
  if (!value || typeof value !== 'object') return false
  const candidate = value as Partial<TokenListToken>
  return (
    typeof candidate.chainId === 'number' &&
    typeof candidate.address === 'string' &&
    typeof candidate.symbol === 'string' &&
    typeof candidate.decimals === 'number'
  )
}

function watchAssetToToken(chainId: number, entry: WatchAssetEntry): TokenListToken {
  return {
    chainId,
    address: entry.options.address,
    symbol: entry.options.symbol,
    name: entry.options.symbol,
    decimals: entry.options.decimals,
    logoURI: entry.options.image,
  }
}

function getApiBase() {
  return resolveExplorerApiBase({
    browserOrigin: '',
    serverFallback: 'https://explorer.d-bis.org',
  }).replace(/\/$/, '')
}

function chainParamsForWallet(chain: WalletChain) {
  return toWalletAddEthereumChainParams(chain, {
    preferSingleRpc: typeof window !== 'undefined' && isMobileWalletContext(),
  })
}

const PRIMARY_IMPORT_CHAIN_IDS = new Set([138, 1])

export default function MultiChainWalletImport() {
  const [status, setStatus] = useState<string | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [progress, setProgress] = useState<{ current: number; total: number; chainId: number } | null>(null)
  const [chains, setChains] = useState<ChainImportState[]>([])
  const [pendingFlow, setPendingFlow] = useState<PendingMultiChainFlow | null>(null)
  const [chainQuery, setChainQuery] = useState('')
  const [showAllChains, setShowAllChains] = useState(false)
  const mobileWalletContext = typeof window !== 'undefined' && isMobileWalletContext()

  const resolveEthereum = () => resolveWalletEthereumProvider(getActiveWalletConnectProvider())

  useEffect(() => {
    let active = true
    const apiBase = getApiBase()

    async function loadChain(chainId: number): Promise<ChainImportState> {
      try {
        const [networkRes, metamaskRes] = await Promise.all([
          fetch(`${apiBase}/api/config/networks`, { cache: 'no-store' }),
          fetch(`${apiBase}/api/v1/config/metamask?chainId=${chainId}`, { cache: 'no-store' }),
        ])
        const networksJson = networkRes.ok ? await networkRes.json() : null
        const metamaskJson = metamaskRes.ok ? await metamaskRes.json() : null
        const networkList = Array.isArray(networksJson?.chains) ? networksJson.chains : []
        const network =
          (metamaskJson as MetaMaskChainPayload | null)?.addEthereumChain ||
          networkList.find((row: WalletChain) => row.chainIdDecimal === chainId) ||
          null
        const watchAssets = Array.isArray((metamaskJson as MetaMaskChainPayload | null)?.watchAssets)
          ? ((metamaskJson as MetaMaskChainPayload).watchAssets ?? [])
          : []
        let tokens = watchAssets.map((entry) => watchAssetToToken(chainId, entry))
        if (tokens.length === 0) {
          const listRes = await fetch(`${apiBase}/api/v1/report/token-list?chainId=${chainId}`, { cache: 'no-store' })
          const listJson = listRes.ok ? await listRes.json() : null
          tokens = (Array.isArray(listJson?.tokens) ? listJson.tokens : []).filter(isTokenListToken)
        }
        return { chainId, network, tokens, loading: false, error: null }
      } catch (e) {
        const message = e instanceof Error ? e.message : 'Failed to load chain metadata'
        return { chainId, network: null, tokens: [], loading: false, error: message }
      }
    }

    void (async () => {
      const initial = WALLET_IMPORT_CHAIN_IDS.map((chainId) => ({
        chainId,
        network: null,
        tokens: [],
        loading: true,
        error: null,
      }))
      if (active) setChains(initial)

      const loaded = await Promise.all(WALLET_IMPORT_CHAIN_IDS.map((chainId) => loadChain(chainId)))
      if (active) setChains(loaded)
    })()

    return () => {
      active = false
    }
  }, [])

  const featuredByChain = useMemo(() => {
    return new Map(
      chains.map((row) => {
        const featuredSymbols = new Set(WALLET_FEATURED_SYMBOLS_BY_CHAIN[row.chainId] ?? [])
        const featured = row.tokens.filter((token) => featuredSymbols.has(token.symbol))
        return [row.chainId, featured.length > 0 ? featured : row.tokens.slice(0, 8)]
      }),
    )
  }, [chains])

  const filteredChains = useMemo(() => {
    const normalizedQuery = chainQuery.trim().toLowerCase()
    if (!normalizedQuery) return chains
    return chains.filter((row) => {
      const label = chainLabel(row.chainId).toLowerCase()
      return label.includes(normalizedQuery) || String(row.chainId).includes(normalizedQuery)
    })
  }, [chainQuery, chains])

  const visibleChains = useMemo(() => {
    if (chainQuery.trim() || showAllChains) return filteredChains
    return filteredChains.filter((row) => PRIMARY_IMPORT_CHAIN_IDS.has(row.chainId))
  }, [chainQuery, filteredChains, showAllChains])

  const hiddenChainCount = Math.max(0, filteredChains.length - visibleChains.length)

  const switchOrAddChain = async (network: WalletChain) => {
    const ethereum = resolveEthereum()
    if (!ethereum) {
      setError('No wallet provider found. Use MetaMask mobile in-app browser or WalletConnect.')
      return false
    }
    try {
      await ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: network.chainId }] })
      return true
    } catch (e) {
      const err = e as { code?: number; message?: string }
      if (err.code !== 4902) {
        setError(err.message || `Failed to switch to ${network.chainName}.`)
        return false
      }
    }
    try {
      await ethereum.request({ method: 'wallet_addEthereumChain', params: [chainParamsForWallet(network)] })
      return true
    } catch (e) {
      const err = e as { message?: string }
      setError(err.message || `Failed to add ${network.chainName}.`)
      return false
    }
  }

  const watchTokensSequentially = async (
    chainId: number,
    tokens: TokenListToken[],
    label: string,
    startIndex = 0,
    priorAdded = 0,
  ) => {
    setError(null)
    if (startIndex === 0) {
      setStatus(null)
      setPendingFlow(null)
    }
    setProgress(null)

    const ethereum = resolveEthereum()
    if (!ethereum) {
      setError('No wallet provider found. Use MetaMask mobile in-app browser or WalletConnect.')
      return
    }

    const row = chains.find((entry) => entry.chainId === chainId)
    if (!row?.network) {
      setError(`Network metadata for ${chainLabel(chainId)} is not available yet.`)
      return
    }

    const switched = await switchOrAddChain(row.network)
    if (!switched) return

    const validTokens = dedupeWalletWatchTokens(tokens.filter(isTokenListToken))
    if (validTokens.length === 0) {
      setError(`No token metadata is available for ${chainLabel(chainId)}.`)
      return
    }

    const batch = await runWatchAssetBatch(ethereum, validTokens, startIndex, {
      mobile: mobileWalletContext,
      onProgress: (current, total) => setProgress({ current, total, chainId }),
    })

    const totalAdded = priorAdded + batch.addedCount
    setProgress(null)

    if (batch.stoppedEarly) {
      setError(batch.errorMessage || 'Token import stopped.')
      setPendingFlow(null)
      setStatus(`${totalAdded} of ${validTokens.length} ${label} requests were accepted before the flow stopped.`)
      return
    }

    if (batch.nextIndex < validTokens.length) {
      setPendingFlow({
        chainId,
        tokens: validTokens,
        label,
        nextIndex: batch.nextIndex,
        totalAdded,
      })
      setStatus(
        `${totalAdded} of ${validTokens.length} on ${chainLabel(chainId)}. Tap Continue on that chain card for the next mobile prompts.`,
      )
      return
    }

    setPendingFlow(null)
    setStatus(`${totalAdded} of ${validTokens.length} ${label} token requests were accepted on ${chainLabel(chainId)}.`)
  }

  const continuePendingFlow = async () => {
    if (!pendingFlow) return
    const { chainId, tokens, label, nextIndex, totalAdded } = pendingFlow
    await watchTokensSequentially(chainId, tokens, label, nextIndex, totalAdded)
  }

  return (
    <div className="space-y-3">
      <ListFilterBar
        query={chainQuery}
        onQueryChange={setChainQuery}
        placeholder="Filter chains by name or chain ID"
        resultCount={visibleChains.length}
        totalCount={chains.length}
      />

      {!chainQuery.trim() && !showAllChains && hiddenChainCount > 0 ? (
        <button
          type="button"
          onClick={() => setShowAllChains(true)}
          className="rounded-lg border border-gray-300 px-3 py-2 text-sm font-medium text-gray-700 hover:border-primary-400 hover:text-primary-700 dark:border-gray-700 dark:text-gray-200"
        >
          Show {hiddenChainCount} more chain{hiddenChainCount === 1 ? '' : 's'}
        </button>
      ) : null}

      <div className="space-y-2">
        {visibleChains.length === 0 ? (
          <p className="text-sm text-gray-600 dark:text-gray-400">No chains match your filter.</p>
        ) : (
          visibleChains.map((row) => {
          const featured = featuredByChain.get(row.chainId) ?? []
          return (
            <DisclosureSection
              key={row.chainId}
              title={`${chainLabel(row.chainId)} · ${row.loading ? '…' : row.tokens.length} tokens`}
              defaultOpen={row.chainId === 138}
              alwaysCollapsible
              className="rounded-lg border border-gray-200 px-3 py-2 dark:border-gray-700"
            >
              <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
                <div className="text-xs text-gray-500 dark:text-gray-400">
                  {row.loading
                    ? 'Loading token metadata…'
                    : row.error
                      ? row.error
                      : `${featured.length} featured · ${row.tokens.length} canonical`}
                </div>
                <div className="flex flex-wrap gap-2">
                  <button
                    type="button"
                    disabled={!row.network || row.loading}
                    onClick={() => row.network && void switchOrAddChain(row.network).then((ok) => ok && setStatus(`Switched to ${chainLabel(row.chainId)}.`))}
                    className={`rounded bg-gray-600 px-3 py-2 text-xs font-medium text-white hover:bg-gray-700 disabled:opacity-50 ${MOBILE_WALLET_BUTTON_CLASS}`}
                  >
                    Switch chain
                  </button>
                  <button
                    type="button"
                    disabled={row.loading || featured.length === 0 || pendingFlow !== null}
                    onClick={() => void watchTokensSequentially(row.chainId, featured, `featured ${chainLabel(row.chainId)}`)}
                    className={`rounded bg-primary-600 px-3 py-2 text-xs font-medium text-white hover:bg-primary-700 disabled:opacity-50 ${MOBILE_WALLET_BUTTON_CLASS}`}
                  >
                    Add featured
                  </button>
                  <button
                    type="button"
                    disabled={row.loading || row.tokens.length === 0 || pendingFlow !== null}
                    onClick={() => void watchTokensSequentially(row.chainId, row.tokens, chainLabel(row.chainId))}
                    className={`rounded bg-gray-700 px-3 py-2 text-xs font-medium text-white hover:bg-gray-800 disabled:opacity-50 ${MOBILE_WALLET_BUTTON_CLASS}`}
                  >
                    Add all
                  </button>
                </div>
              </div>
              {progress?.chainId === row.chainId ? (
                <div className="mt-2 text-xs text-gray-600 dark:text-gray-400">
                  Prompt {progress.current} of {progress.total}
                </div>
              ) : null}
            </DisclosureSection>
          )
        })
        )}
      </div>

      {pendingFlow ? (
        <button
          type="button"
          onClick={() => void continuePendingFlow()}
          className={`rounded bg-amber-600 px-3 py-2 text-sm font-medium text-white hover:bg-amber-700 ${MOBILE_WALLET_BUTTON_CLASS}`}
        >
          Continue {chainLabel(pendingFlow.chainId)} ({pendingFlow.totalAdded} added, {pendingFlow.tokens.length - pendingFlow.nextIndex} left)
        </button>
      ) : null}

      {status ? <p className="text-sm text-green-600 dark:text-green-400">{status}</p> : null}
      {error ? <p className="text-sm text-red-600 dark:text-red-400">{error}</p> : null}
    </div>
  )
}
