import { fetchBlockscoutJson, normalizeAddressTokenTransfer, type BlockscoutTokenTransfer } from './blockscout'
import { configApi, type TokenListToken } from './config'
import { routesApi, type MissionControlLiquidityPool } from './routes'
import type { AddressTokenTransfer } from './addresses'

export interface TokenProfile {
  address: string
  name?: string
  symbol?: string
  decimals: number
  type?: string
  total_supply?: string
  holders?: number
  exchange_rate?: string | number | null
  icon_url?: string | null
  circulating_market_cap?: string | number | null
  volume_24h?: string | number | null
}

export interface TokenHolder {
  address: string
  label?: string
  value: string
  token_decimals: number
}

export interface TokenProvenance {
  listed: boolean
  chainId?: number
  name?: string
  symbol?: string
  logoURI?: string
  tags: string[]
}

function normalizeTokenProfile(raw: {
  address: string
  name?: string | null
  symbol?: string | null
  decimals?: string | number | null
  type?: string | null
  total_supply?: string | null
  holders?: string | number | null
  exchange_rate?: string | number | null
  icon_url?: string | null
  circulating_market_cap?: string | number | null
  volume_24h?: string | number | null
}): TokenProfile {
  return {
    address: raw.address,
    name: raw.name || undefined,
    symbol: raw.symbol || undefined,
    decimals: Number(raw.decimals || 0),
    type: raw.type || undefined,
    total_supply: raw.total_supply || undefined,
    holders: raw.holders != null ? Number(raw.holders) : undefined,
    exchange_rate: raw.exchange_rate ?? null,
    icon_url: raw.icon_url ?? null,
    circulating_market_cap: raw.circulating_market_cap ?? null,
    volume_24h: raw.volume_24h ?? null,
  }
}

function normalizeTokenHolder(raw: {
  address?: {
    hash?: string | null
    name?: string | null
    label?: string | null
  } | null
  value?: string | null
  token?: {
    decimals?: string | number | null
  } | null
}): TokenHolder {
  return {
    address: raw.address?.hash || '',
    label: raw.address?.name || raw.address?.label || undefined,
    value: raw.value || '0',
    token_decimals: Number(raw.token?.decimals || 0),
  }
}

async function getTokenListLookup(): Promise<Map<string, TokenListToken>> {
  const response = await configApi.getTokenList()
  const lookup = new Map<string, TokenListToken>()
  for (const token of response.tokens || []) {
    if (token.address) {
      lookup.set(token.address.toLowerCase(), token)
    }
  }
  return lookup
}

export const tokensApi = {
  getSafe: async (address: string): Promise<{ ok: boolean; data: TokenProfile | null }> => {
    try {
      const raw = await fetchBlockscoutJson<{
        address: string
        name?: string | null
        symbol?: string | null
        decimals?: string | number | null
        type?: string | null
        total_supply?: string | null
        holders?: string | number | null
        exchange_rate?: string | number | null
        icon_url?: string | null
        circulating_market_cap?: string | number | null
        volume_24h?: string | number | null
      }>(`/api/v2/tokens/${address}`)
      return { ok: true, data: normalizeTokenProfile(raw) }
    } catch {
      return { ok: false, data: null }
    }
  },

  getTransfersSafe: async (
    address: string,
    page = 1,
    pageSize = 10
  ): Promise<{ ok: boolean; data: AddressTokenTransfer[] }> => {
    try {
      const params = new URLSearchParams({
        page: page.toString(),
        items_count: pageSize.toString(),
      })
      const raw = await fetchBlockscoutJson<{ items?: BlockscoutTokenTransfer[] }>(
        `/api/v2/tokens/${address}/transfers?${params.toString()}`
      )
      return {
        ok: true,
        data: Array.isArray(raw.items) ? raw.items.map((item) => normalizeAddressTokenTransfer(item)) : [],
      }
    } catch {
      return { ok: false, data: [] }
    }
  },

  getHoldersSafe: async (
    address: string,
    page = 1,
    pageSize = 10
  ): Promise<{ ok: boolean; data: TokenHolder[] }> => {
    try {
      const params = new URLSearchParams({
        page: page.toString(),
        items_count: pageSize.toString(),
      })
      const raw = await fetchBlockscoutJson<{ items?: Array<{
        address?: { hash?: string | null; name?: string | null; label?: string | null } | null
        value?: string | null
        token?: { decimals?: string | number | null } | null
      }> }>(`/api/v2/tokens/${address}/holders?${params.toString()}`)
      return {
        ok: true,
        data: Array.isArray(raw.items) ? raw.items.map((item) => normalizeTokenHolder(item)) : [],
      }
    } catch {
      return { ok: false, data: [] }
    }
  },

  getProvenanceSafe: async (address: string): Promise<{ ok: boolean; data: TokenProvenance | null }> => {
    try {
      const lookup = await getTokenListLookup()
      const token = lookup.get(address.toLowerCase())
      if (!token) {
        return { ok: true, data: { listed: false, tags: [] } }
      }
      return {
        ok: true,
        data: {
          listed: true,
          chainId: token.chainId,
          name: token.name,
          symbol: token.symbol,
          logoURI: token.logoURI,
          tags: token.tags || [],
        },
      }
    } catch {
      return { ok: false, data: null }
    }
  },

  listCuratedSafe: async (chainId = 138): Promise<{ ok: boolean; data: TokenListToken[] }> => {
    try {
      const response = await configApi.getTokenList()
      const data = (response.tokens || [])
        .filter((token) => token.chainId === chainId && typeof token.address === 'string' && token.address.trim().length > 0)
        .sort((left, right) => (left.symbol || left.name || '').localeCompare(right.symbol || right.name || ''))
      return { ok: true, data }
    } catch {
      return { ok: false, data: [] }
    }
  },

  getRelatedPoolsSafe: async (address: string): Promise<{ ok: boolean; data: MissionControlLiquidityPool[] }> => {
    try {
      const response = await routesApi.getTokenPools(address)
      return { ok: true, data: response.pools || [] }
    } catch {
      return { ok: false, data: [] }
    }
  },
}
