import type { AddressTokenBalance } from '@/services/api/addresses'
import type { TokenAggregationCashInBankAudit, TokenAggregationHoReserveCoverage, TokenAggregationMarketSnapshot, TokenAggregationSupplyNostroMatch, TokenAggregationTokenSnapshot } from '@/services/api/tokenAggregation'

export interface ResolvedTokenMarket {
  priceUsd?: number
  liquidityUsd?: number
  cashLiquidityUsd?: number
  cashInBankUsd?: number
  cashInBankAudit?: TokenAggregationCashInBankAudit
  totalSupplyUsd?: number
  supplyNostroMatch?: TokenAggregationSupplyNostroMatch
  hoReserveCoverage?: TokenAggregationHoReserveCoverage
  lastUpdated?: string | null
  source?: 'token-aggregation' | 'blockscout' | 'derived'
  pricingKind?: 'spot' | 'lp-share'
  isCanonicalClone?: boolean
}

/** Wallet liquidity stack + HO audit freshness (API market or resolved balance). */
export type TokenLiquidityMarketView = Pick<
  TokenAggregationMarketSnapshot,
  | 'cashLiquidityUsd'
  | 'cashInBankUsd'
  | 'cashInBankAudit'
  | 'supplyNostroMatch'
  | 'hoReserveCoverage'
  | 'lastUpdated'
>

function normalizeLastUpdated(value: string | null | undefined): string | undefined {
  return value ?? undefined
}

export function isLikelyLpReceiptSymbol(symbol: string | undefined): boolean {
  if (!symbol) return false
  const upper = symbol.toUpperCase()
  return upper.includes('DLP') || upper.endsWith('-LP') || upper.startsWith('LP-') || upper === 'LP'
}

export function formatBalanceUsdLabel(
  priceUsd: number | undefined,
  balanceUsd: number | undefined,
  options?: { pricingKind?: 'spot' | 'lp-share'; atTransfer?: boolean },
): string {
  if (options?.pricingKind === 'lp-share') {
    return 'LP share — USD N/A'
  }
  if (balanceUsd != null) {
    return options?.atTransfer ? `≈ ${formatUsd(balanceUsd)} (at transfer)` : `≈ ${formatUsd(balanceUsd)}`
  }
  if (priceUsd == null) {
    return 'USD unavailable'
  }
  return 'USD unavailable'
}

function formatUsd(value: number): string {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
    maximumFractionDigits: value >= 100 ? 0 : 2,
  }).format(value)
}

function parseUsd(value: string | number | null | undefined): number | undefined {
  if (value == null) return undefined
  const numeric = typeof value === 'number' ? value : Number(value)
  return Number.isFinite(numeric) && numeric > 0 ? numeric : undefined
}

export function resolveTokenMarketForBalance(
  balance: Pick<AddressTokenBalance, 'token_address' | 'exchange_rate' | 'token_symbol'>,
  tokenMarkets: Record<string, TokenAggregationTokenSnapshot>,
): ResolvedTokenMarket {
  const key = balance.token_address.toLowerCase()
  const aggregationSnapshot = tokenMarkets[key]
  const aggregationMarket: TokenAggregationMarketSnapshot | null | undefined = aggregationSnapshot?.market
  const aggregationPrice = aggregationMarket?.priceUsd
  const blockscoutPrice = parseUsd(balance.exchange_rate)
  const pricingKind = aggregationMarket?.pricingKind
    ?? (isLikelyLpReceiptSymbol(balance.token_symbol ?? aggregationSnapshot?.symbol) ? 'lp-share' : 'spot')
  const isCanonicalClone = aggregationMarket?.isCanonicalClone

  if (aggregationPrice != null && aggregationPrice > 0) {
    return {
      priceUsd: aggregationPrice,
      liquidityUsd: aggregationMarket?.liquidityUsd,
      cashLiquidityUsd: aggregationMarket?.cashLiquidityUsd,
      cashInBankUsd: aggregationMarket?.cashInBankUsd,
      cashInBankAudit: aggregationMarket?.cashInBankAudit,
      totalSupplyUsd: aggregationMarket?.totalSupplyUsd,
      supplyNostroMatch: aggregationMarket?.supplyNostroMatch,
      hoReserveCoverage: aggregationMarket?.hoReserveCoverage,
      lastUpdated: normalizeLastUpdated(aggregationMarket?.lastUpdated ?? aggregationMarket?.hoReserveCoverage?.generatedAt),
      source: 'token-aggregation',
      pricingKind,
      isCanonicalClone,
    }
  }

  if (blockscoutPrice != null) {
    return {
      priceUsd: blockscoutPrice,
      liquidityUsd: aggregationMarket?.liquidityUsd,
      cashLiquidityUsd: aggregationMarket?.cashLiquidityUsd,
      cashInBankUsd: aggregationMarket?.cashInBankUsd,
      cashInBankAudit: aggregationMarket?.cashInBankAudit,
      totalSupplyUsd: aggregationMarket?.totalSupplyUsd,
      supplyNostroMatch: aggregationMarket?.supplyNostroMatch,
      hoReserveCoverage: aggregationMarket?.hoReserveCoverage,
      lastUpdated: normalizeLastUpdated(aggregationMarket?.lastUpdated ?? aggregationMarket?.hoReserveCoverage?.generatedAt),
      source: 'blockscout',
      pricingKind,
      isCanonicalClone,
    }
  }

  return {
    priceUsd: undefined,
    liquidityUsd: aggregationMarket?.liquidityUsd,
    cashLiquidityUsd: aggregationMarket?.cashLiquidityUsd,
    cashInBankUsd: aggregationMarket?.cashInBankUsd,
    cashInBankAudit: aggregationMarket?.cashInBankAudit,
    totalSupplyUsd: aggregationMarket?.totalSupplyUsd,
    supplyNostroMatch: aggregationMarket?.supplyNostroMatch,
    hoReserveCoverage: aggregationMarket?.hoReserveCoverage,
    lastUpdated: normalizeLastUpdated(aggregationMarket?.lastUpdated ?? aggregationMarket?.hoReserveCoverage?.generatedAt),
    source: 'derived',
    pricingKind,
    isCanonicalClone,
  }
}

/** Public-facing cash pool depth — never fall back to full DEX pool TVL. */
export function resolveDisplayedCashLiquidityUsd(
  market: Pick<ResolvedTokenMarket, 'cashLiquidityUsd'>,
): number | undefined {
  const value = market.cashLiquidityUsd
  return value != null && value > 0 ? value : undefined
}

export function formatCashLiquidityLabel(value: number | undefined): string {
  if (value == null) return 'Cash liq. unavailable'
  return formatUsd(value)
}

export function resolveDisplayedCashInBankUsd(
  market: Pick<ResolvedTokenMarket, 'cashInBankUsd'>,
): number | undefined {
  const value = market.cashInBankUsd
  return value != null && value > 0 ? value : undefined
}

export function formatCashInBankAuditLabel(
  audit: TokenAggregationCashInBankAudit | undefined,
): string {
  if (!audit) return 'audit pending'
  switch (audit.status) {
    case 'aligned':
      return 'audit aligned'
    case 'breaks':
      return audit.breaksCount > 0 ? `audit breaks (${audit.breaksCount})` : 'audit breaks'
    case 'pending':
      return 'audit pending'
    case 'unavailable':
      return 'audit unavailable'
    default:
      return 'audit pending'
  }
}

export function formatSupplyNostroMatchLabel(
  match: TokenAggregationSupplyNostroMatch | undefined,
): string {
  if (!match) return 'supply match pending'
  const ho = match.headOfficeId ?? 1
  switch (match.status) {
    case 'matched':
      return `HO-${ho} supply matched (${match.multiplier}× nostro)`
    case 'break':
      return `HO-${ho} supply break (${match.multiplier}× rule)`
    case 'pending':
      return 'supply match pending'
    case 'unavailable':
      return 'supply match unavailable'
    default:
      return 'supply match pending'
  }
}

/** Explicit Total supply vs 1.2× HO cash-in-bank backing line. */
export function formatTotalSupplyHoBackingLabel(
  totalSupplyUsd: number | undefined,
  match: TokenAggregationSupplyNostroMatch | undefined,
): string {
  if (totalSupplyUsd == null) return 'Total supply USD unavailable'
  if (!match || match.expectedSupplyUsd == null) {
    return `Total supply ${formatUsd(totalSupplyUsd)} · HO backing check pending`
  }
  const ho = match.headOfficeId ?? 1
  const mult = match.multiplier ?? 1.2
  const status =
    match.status === 'matched' ? 'matched' : match.status === 'break' ? 'break' : match.status
  return `Total supply ${formatUsd(totalSupplyUsd)} · expected ${formatUsd(match.expectedSupplyUsd)} (${mult}× HO-${ho} nostro ${formatUsd(match.externalNostroCashUsd ?? 0)}) · ${status}`
}

export function formatHoReserveCoverageLabel(
  coverage: TokenAggregationHoReserveCoverage | undefined,
): string {
  if (!coverage || !(coverage.coverageRatio > 0)) {
    return coverage?.status === 'pending' ? 'pending' : 'unavailable'
  }
  const mult = coverage.multiplier ?? 1.2
  const ratio = coverage.coverageRatio.toFixed(2)
  const gl = coverage.reserveAssetGlCodes?.join('+') ?? '1000+1050'
  const statusSuffix =
    coverage.status === 'sufficient'
      ? ''
      : coverage.status === 'insufficient'
        ? ' · insufficient'
        : ` · ${coverage.status}`
  return `${ratio}× (${mult}× rule · HO GL ${gl})${statusSuffix}`
}

/** Fineract audit freshness for HO nostro / reserve lines. */
export function resolveHoLiquidityGeneratedAt(
  market: Pick<TokenLiquidityMarketView, 'hoReserveCoverage' | 'lastUpdated'>,
): string | undefined {
  const candidates = [
    market.hoReserveCoverage?.generatedAt,
    normalizeLastUpdated(market.lastUpdated),
  ].filter(Boolean) as string[]
  if (candidates.length === 0) return undefined
  return candidates.sort().reverse()[0]
}

export function formatHoLiquidityAuditFreshness(
  market: Pick<TokenLiquidityMarketView, 'hoReserveCoverage' | 'lastUpdated'>,
): string {
  const at = resolveHoLiquidityGeneratedAt(market)
  if (!at) return 'Fineract audit pending'
  const ageMs = Date.now() - new Date(at).getTime()
  if (!Number.isFinite(ageMs) || ageMs < 0) return 'Fineract audit pending'
  const ageMin = Math.round(ageMs / 60_000)
  if (ageMin < 1) return 'Fineract audit · just now'
  if (ageMin < 60) return `Fineract audit · ${ageMin}m ago`
  const ageH = Math.round(ageMin / 60)
  return `Fineract audit · ${ageH}h ago`
}

export function formatCashInBankLabel(
  value: number | undefined,
  audit: TokenAggregationCashInBankAudit | undefined,
  supplyNostroMatch?: TokenAggregationSupplyNostroMatch,
): string {
  if (value == null) return 'Cash-in-bank unavailable'
  const supplyLabel = formatSupplyNostroMatchLabel(supplyNostroMatch)
  return `${formatUsd(value)} (${formatCashInBankAuditLabel(audit)} · ${supplyLabel})`
}

export function estimateTokenBalanceUsd(
  rawAmount: string,
  decimals: number,
  priceUsd: number | undefined,
): number | undefined {
  if (priceUsd == null || !(priceUsd > 0) || !rawAmount) return undefined
  try {
    const raw = BigInt(rawAmount)
    if (raw <= 0n) return 0
    const scale = 10n ** BigInt(decimals)
    const whole = raw / scale
    const fraction = raw % scale
    const amount = Number(whole) + Number(fraction) / Number(scale)
    if (!Number.isFinite(amount)) return undefined
    return amount * priceUsd
  } catch {
    return undefined
  }
}
