import { resolveExplorerApiBase } from '../../../libs/frontend-api-client/api-base'

export interface TokenAggregationMarketSnapshot {
  priceUsd?: number
  volume24h?: number
  liquidityUsd?: number
  lastUpdated?: string | null
}

export interface TokenAggregationTokenSnapshot {
  chainId: number
  address: string
  name?: string
  symbol?: string
  decimals?: number
  totalSupply?: string
  market?: TokenAggregationMarketSnapshot | null
}

export interface TokenAggregationHistoricalPriceSnapshot {
  chainId: number
  tokenAddress: string
  requestedTimestamp: string
  effectiveTimestamp?: string
  priceUsd?: number
  source?: string
}

export interface CheckpointTransferUsdLine {
  tokenAddress?: string
  token?: string
  tokenSymbol?: string
  symbol?: string
  amountRaw?: string
  decimals?: number
  valueUsd?: string
  priceSource?: string
}

export interface CheckpointTxAttestationSnapshot {
  txHash: string
  included: boolean
  batchId: string
  batchTotalUsd?: string
  leaf: {
    valueUsd?: string
    nativeValueUsd?: string
    tokenValueUsd?: string
    totalTransfersUsd?: string
    priceSource?: string
    transfers?: CheckpointTransferUsdLine[]
  } | null
  source?: string
}

interface RawTokenAggregationTokenResponse {
  token?: {
    chainId?: number | string | null
    address?: string | null
    name?: string | null
    symbol?: string | null
    decimals?: number | string | null
    totalSupply?: string | null
    market?: {
      priceUsd?: number | string | null
      volume24h?: number | string | null
      liquidityUsd?: number | string | null
      lastUpdated?: string | null
    } | null
  } | null
}

interface RawTokenAggregationHistoricalPriceResponse {
  chainId?: number | string | null
  tokenAddress?: string | null
  requestedTimestamp?: string | null
  effectiveTimestamp?: string | null
  priceUsd?: number | string | null
  source?: string | null
}

function toNumber(value: number | string | null | undefined): number | undefined {
  if (typeof value === 'number' && Number.isFinite(value)) return value
  if (typeof value === 'string' && value.trim() !== '') {
    const parsed = Number(value)
    if (Number.isFinite(parsed)) return parsed
  }
  return undefined
}

function decimalStringToNumber(value: string | null | undefined, decimals: number | undefined): number | null {
  if (!value || decimals == null || decimals < 0) return null
  try {
    const raw = BigInt(value)
    if (raw <= 0n) return 0
    const scale = 10n ** BigInt(decimals)
    const whole = raw / scale
    const fraction = raw % scale
    const normalized = Number(whole) + Number(fraction) / Number(scale)
    return Number.isFinite(normalized) ? normalized : null
  } catch {
    return null
  }
}

function normalizePossiblyRawLiquidityUsd(
  liquidityUsd: number | undefined,
  totalSupply: string | null | undefined,
  decimals: number | undefined,
  priceUsd: number | undefined,
): number | undefined {
  if (liquidityUsd == null || !(liquidityUsd > 0)) return liquidityUsd
  if (!Number.isInteger(decimals) || decimals == null || decimals <= 0) return liquidityUsd

  const supplyUnits = decimalStringToNumber(totalSupply, decimals)
  const price = priceUsd != null && priceUsd > 0 ? priceUsd : 1
  if (supplyUnits == null || supplyUnits <= 0) return liquidityUsd

  const plausibleSupplyValue = supplyUnits * price
  const normalizedLiquidity = liquidityUsd / 10 ** decimals

  if (
    Number.isFinite(plausibleSupplyValue) &&
    Number.isFinite(normalizedLiquidity) &&
    liquidityUsd > plausibleSupplyValue &&
    normalizedLiquidity <= plausibleSupplyValue
  ) {
    return normalizedLiquidity
  }

  return liquidityUsd
}

function normalizeTokenSnapshot(raw: RawTokenAggregationTokenResponse): TokenAggregationTokenSnapshot | null {
  const token = raw.token
  if (!token?.address) {
    return null
  }

  const decimals = toNumber(token.decimals)
  const priceUsd = toNumber(token.market?.priceUsd)
  const liquidityUsd = normalizePossiblyRawLiquidityUsd(
    toNumber(token.market?.liquidityUsd),
    token.totalSupply,
    decimals,
    priceUsd,
  )

  return {
    chainId: toNumber(token.chainId) ?? 138,
    address: token.address,
    name: token.name || undefined,
    symbol: token.symbol || undefined,
    decimals,
    totalSupply: token.totalSupply || undefined,
    market: token.market
      ? {
          priceUsd,
          volume24h: toNumber(token.market.volume24h),
          liquidityUsd,
          lastUpdated: token.market.lastUpdated || null,
        }
      : null,
  }
}

function normalizeHistoricalPriceSnapshot(
  raw: RawTokenAggregationHistoricalPriceResponse,
): TokenAggregationHistoricalPriceSnapshot | null {
  if (!raw.tokenAddress || !raw.requestedTimestamp) {
    return null
  }

  return {
    chainId: toNumber(raw.chainId) ?? 138,
    tokenAddress: raw.tokenAddress,
    requestedTimestamp: raw.requestedTimestamp,
    effectiveTimestamp: raw.effectiveTimestamp || undefined,
    priceUsd: toNumber(raw.priceUsd),
    source: raw.source || undefined,
  }
}

function getTokenAggregationBase(): string {
  return `${resolveExplorerApiBase()}/token-aggregation/api/v1`
}

export const tokenAggregationApi = {
  getTokenSafe: async (chainId: number, address: string): Promise<{ ok: boolean; data: TokenAggregationTokenSnapshot | null }> => {
    try {
      const response = await fetch(`${getTokenAggregationBase()}/tokens/${address}?chainId=${chainId}`)
      if (!response.ok) {
        return { ok: false, data: null }
      }
      const raw = (await response.json()) as RawTokenAggregationTokenResponse
      return { ok: true, data: normalizeTokenSnapshot(raw) }
    } catch {
      return { ok: false, data: null }
    }
  },

  getTokensByAddressSafe: async (
    chainId: number,
    addresses: string[],
  ): Promise<{ ok: boolean; data: TokenAggregationTokenSnapshot[] }> => {
    const uniqueAddresses = [...new Set(addresses.map((address) => address.trim()).filter(Boolean))]
    if (uniqueAddresses.length === 0) {
      return { ok: true, data: [] }
    }

    const results = await Promise.all(uniqueAddresses.map((address) => tokenAggregationApi.getTokenSafe(chainId, address)))
    const data = results
      .filter((result): result is { ok: true; data: TokenAggregationTokenSnapshot | null } => result.ok)
      .map((result) => result.data)
      .filter((snapshot): snapshot is TokenAggregationTokenSnapshot => Boolean(snapshot?.address))

    return { ok: data.length > 0, data }
  },

  getCheckpointAttestationSafe: async (
    txHash: string,
  ): Promise<{ ok: boolean; data: CheckpointTxAttestationSnapshot | null }> => {
    try {
      const response = await fetch(
        `${getTokenAggregationBase()}/checkpoint/tx/${txHash}/attestation`,
      )
      if (!response.ok) {
        return { ok: false, data: null }
      }
      const raw = (await response.json()) as CheckpointTxAttestationSnapshot
      return { ok: true, data: raw }
    } catch {
      return { ok: false, data: null }
    }
  },

  getPriceAtSafe: async (
    chainId: number,
    address: string,
    timestamp: string,
  ): Promise<{ ok: boolean; data: TokenAggregationHistoricalPriceSnapshot | null }> => {
    try {
      const response = await fetch(
        `${getTokenAggregationBase()}/tokens/${address}/price-at?chainId=${chainId}&timestamp=${encodeURIComponent(timestamp)}`
      )
      if (!response.ok) {
        return { ok: false, data: null }
      }
      const raw = (await response.json()) as RawTokenAggregationHistoricalPriceResponse
      return { ok: true, data: normalizeHistoricalPriceSnapshot(raw) }
    } catch {
      return { ok: false, data: null }
    }
  },
}
