import { getNativeAssetMarketSafe, estimateNativeUsdValue } from '@/services/api/nativeAssetPricing'
import { tokenAggregationApi, type TokenAggregationCashInBankAudit, type TokenAggregationHoReserveCoverage, type TokenAggregationSupplyNostroMatch, type TokenAggregationTokenSnapshot } from '@/services/api/tokenAggregation'
import { formatTokenAmount } from '@/utils/format'
import { estimateTokenBalanceUsd } from '@/utils/tokenMarket'
import {
  formatNativeEthFromWei,
  listTokensWithNonZeroBalance,
  readNativeBalanceRaw,
  type EthereumRpcProvider,
} from '@/utils/walletTokenBalances'

export const CHAIN138_NATIVE_ETH_LOGO =
  'https://explorer.d-bis.org/api/v1/report/logo/ETH?v=20260510'

export type FundedWalletCatalogToken = {
  chainId: number
  address: string
  symbol: string
  name: string
  decimals: number
  logoURI?: string
}

export type FundedWalletTokenRow = {
  kind: 'native' | 'erc20'
  symbol: string
  name: string
  address: string | null
  decimals: number
  logoURI: string
  balanceRaw: string
  balanceLabel: string
  priceUsd?: number
  balanceUsd?: number
  liquidityUsd?: number
  cashLiquidityUsd?: number
  cashInBankUsd?: number
  cashInBankAudit?: TokenAggregationCashInBankAudit
  supplyNostroMatch?: TokenAggregationSupplyNostroMatch
  hoReserveCoverage?: TokenAggregationHoReserveCoverage
  pricingKind?: string
  marketSource?: string
  metaMaskAddable: boolean
}

function formatListingUsd(value: number | undefined): string {
  if (value == null || !Number.isFinite(value)) return 'USD unavailable'
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
    maximumFractionDigits: value >= 100 ? 0 : 2,
  }).format(value)
}

export function formatFundedRowUnitPrice(row: Pick<FundedWalletTokenRow, 'priceUsd' | 'pricingKind'>): string {
  if (row.pricingKind === 'lp-share') return 'LP share — USD N/A'
  if (row.priceUsd == null) return 'USD unavailable'
  return formatListingUsd(row.priceUsd)
}

export function formatFundedRowBalanceUsd(row: Pick<FundedWalletTokenRow, 'balanceUsd' | 'pricingKind'>): string {
  if (row.pricingKind === 'lp-share') return 'LP share — USD N/A'
  if (row.balanceUsd == null) return 'USD unavailable'
  return `≈ ${formatListingUsd(row.balanceUsd)}`
}

function marketMapFromSnapshots(snapshots: TokenAggregationTokenSnapshot[]): Map<string, TokenAggregationTokenSnapshot> {
  const map = new Map<string, TokenAggregationTokenSnapshot>()
  for (const snapshot of snapshots) {
    if (snapshot.address) map.set(snapshot.address.toLowerCase(), snapshot)
  }
  return map
}

/** On-chain funded rows for native ETH + catalog ERC-20 entries with balance &gt; 0. */
export async function buildFundedWalletTokenRows(
  provider: EthereumRpcProvider,
  walletAddress: string,
  catalogTokens: FundedWalletCatalogToken[],
  options?: {
    nativeLogoUri?: string
    onProgress?: (current: number, total: number) => void
  },
): Promise<FundedWalletTokenRow[]> {
  const nativeLogoUri = options?.nativeLogoUri || CHAIN138_NATIVE_ETH_LOGO
  const nativeWei = await readNativeBalanceRaw(provider, walletAddress)

  const fundedErc20 = await listTokensWithNonZeroBalance(
    provider,
    walletAddress,
    catalogTokens,
    options?.onProgress,
  )

  const rows: FundedWalletTokenRow[] = []

  if (nativeWei > 0n) {
    rows.push({
      kind: 'native',
      symbol: 'ETH',
      name: 'Ether',
      address: null,
      decimals: 18,
      logoURI: nativeLogoUri,
      balanceRaw: nativeWei.toString(),
      balanceLabel: formatNativeEthFromWei(nativeWei),
      metaMaskAddable: false,
    })
  }

  for (const token of fundedErc20) {
    rows.push({
      kind: 'erc20',
      symbol: token.symbol,
      name: token.name,
      address: token.address,
      decimals: token.decimals,
      logoURI: token.logoURI || CHAIN138_NATIVE_ETH_LOGO,
      balanceRaw: token.balanceRaw,
      balanceLabel: formatTokenAmount(token.balanceRaw, token.decimals, token.symbol, 6),
      metaMaskAddable: true,
    })
  }

  return rows
}

/** Attach token-aggregation spot prices and USD notionals to funded rows. */
export async function enrichFundedWalletTokenRowsWithMarket(
  rows: FundedWalletTokenRow[],
  chainId = 138,
): Promise<FundedWalletTokenRow[]> {
  if (rows.length === 0) return rows

  const erc20Addresses = rows
    .filter((row) => row.kind === 'erc20' && row.address)
    .map((row) => row.address as string)

  const [nativeMarket, erc20Market] = await Promise.all([
    getNativeAssetMarketSafe(chainId),
    tokenAggregationApi.getTokensByAddressSafe(chainId, erc20Addresses),
  ])

  const markets = marketMapFromSnapshots(erc20Market.ok ? erc20Market.data : [])
  const nativePriceUsd = nativeMarket.ok ? nativeMarket.data?.market?.priceUsd : undefined

  return rows.map((row) => {
    if (row.kind === 'native') {
      const balanceUsdText = estimateNativeUsdValue(row.balanceRaw, nativePriceUsd)
      return {
        ...row,
        priceUsd: nativePriceUsd,
        balanceUsd: balanceUsdText != null ? Number(balanceUsdText) : undefined,
        liquidityUsd: nativeMarket.data?.market?.liquidityUsd,
        cashLiquidityUsd: nativeMarket.data?.market?.cashLiquidityUsd,
        cashInBankUsd: nativeMarket.data?.market?.cashInBankUsd,
        cashInBankAudit: nativeMarket.data?.market?.cashInBankAudit,
        supplyNostroMatch: nativeMarket.data?.market?.supplyNostroMatch,
        hoReserveCoverage: nativeMarket.data?.market?.hoReserveCoverage,
        pricingKind: nativeMarket.data?.market?.pricingKind,
        marketSource: nativeMarket.ok && nativeMarket.data?.market ? 'token-aggregation' : undefined,
      }
    }

    const market = row.address ? markets.get(row.address.toLowerCase())?.market : undefined
    const priceUsd = market?.priceUsd
    const balanceUsd = estimateTokenBalanceUsd(row.balanceRaw, row.decimals, priceUsd)

    return {
      ...row,
      priceUsd,
      balanceUsd,
      liquidityUsd: market?.liquidityUsd,
      cashLiquidityUsd: market?.cashLiquidityUsd,
      cashInBankUsd: market?.cashInBankUsd,
      cashInBankAudit: market?.cashInBankAudit,
      supplyNostroMatch: market?.supplyNostroMatch,
      hoReserveCoverage: market?.hoReserveCoverage,
      pricingKind: market?.pricingKind,
      marketSource: market ? 'token-aggregation' : undefined,
    }
  })
}

export function sortFundedWalletTokenRows(rows: FundedWalletTokenRow[]): FundedWalletTokenRow[] {
  return [...rows].sort((left, right) => {
    if (left.kind === 'native') return -1
    if (right.kind === 'native') return 1
    const leftUsd = left.balanceUsd ?? 0
    const rightUsd = right.balanceUsd ?? 0
    if (rightUsd !== leftUsd) return rightUsd - leftUsd
    return left.symbol.localeCompare(right.symbol)
  })
}

export function fundedRowsToWatchCatalogTokens(rows: FundedWalletTokenRow[]): FundedWalletCatalogToken[] {
  return rows
    .filter((row) => row.kind === 'erc20' && row.address && row.metaMaskAddable)
    .map((row) => ({
      chainId: 138,
      address: row.address as string,
      symbol: row.symbol,
      name: row.name,
      decimals: row.decimals,
      logoURI: row.logoURI,
    }))
}

export async function loadFundedWalletTokenListing(
  provider: EthereumRpcProvider,
  walletAddress: string,
  catalogTokens: FundedWalletCatalogToken[],
  options?: {
    nativeLogoUri?: string
    onProgress?: (current: number, total: number) => void
  },
): Promise<FundedWalletTokenRow[]> {
  const rows = await buildFundedWalletTokenRows(provider, walletAddress, catalogTokens, options)
  const enriched = await enrichFundedWalletTokenRowsWithMarket(rows)
  return sortFundedWalletTokenRows(enriched)
}
