import { getCanonicalTokenByAddress } from '../config/canonical-tokens';
import { MarketDataRepository } from '../database/repositories/market-data-repo';
import { getChain138EthUsdOnChain } from './chain138-onchain-reference-prices';
import { resolveCanonicalPriceUsd } from './canonical-price-oracle';
import { ensureIso4217FxPrimed } from './iso4217-fx-refresh';

const marketDataRepo = new MarketDataRepository();

export type Iso4217SpotPrice = {
  priceUsd?: number;
  symbol?: string;
  currencyCode?: string;
  referenceSymbol?: string;
  source: 'market-data' | 'iso4217-oracle' | 'native-oracle' | 'unresolved';
};

const NATIVE_ETH_SENTINELS = new Set([
  '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
  '0x0000000000000000000000000000000000000000',
]);

const CHAIN138_ETH_USD_ORACLE_PROXY = '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6';
const CHAIN138_ETH_USD_ORACLE_AGGREGATOR = '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506';
const CHAIN138_WETH9 = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';

function isChain138EthUsdPegToken(chainId: number, normalized: string, symbol?: string): boolean {
  if (chainId !== 138) return false;
  if (normalized === CHAIN138_WETH9) return true;
  const sym = symbol?.toUpperCase();
  return sym === 'WETH' || sym === 'WETH9' || sym === 'WETH10' || sym === 'CETH' || sym === 'CETHL2';
}

async function resolveEthUsdFeedPrice(chainId: number, normalized: string, symbol?: string): Promise<Iso4217SpotPrice | undefined> {
  if (chainId !== 138) return undefined;
  const isFeed =
    normalized === CHAIN138_ETH_USD_ORACLE_PROXY ||
    normalized === CHAIN138_ETH_USD_ORACLE_AGGREGATOR ||
    symbol?.toUpperCase() === 'ETH-USD';
  if (!isFeed) return undefined;

  const native = await getChain138EthUsdOnChain();
  if (native?.priceUsd && native.priceUsd > 0) {
    return {
      priceUsd: native.priceUsd,
      symbol: 'ETH-USD',
      currencyCode: 'USD',
      referenceSymbol: 'ETH',
      source: 'native-oracle',
    };
  }
  return undefined;
}

export async function resolveIso4217SpotPrice(chainId: number, address: string): Promise<Iso4217SpotPrice> {
  await ensureIso4217FxPrimed();

  const normalized = address.trim().toLowerCase();
  if (NATIVE_ETH_SENTINELS.has(normalized)) {
    const native = chainId === 138 ? await getChain138EthUsdOnChain() : undefined;
    if (native?.priceUsd && native.priceUsd > 0) {
      return {
        priceUsd: native.priceUsd,
        symbol: 'ETH',
        currencyCode: 'ETH',
        referenceSymbol: 'ETH',
        source: 'native-oracle',
      };
    }
    const ethFallback = resolveCanonicalPriceUsd(chainId, normalized);
    return {
      priceUsd: ethFallback.priceUsd,
      symbol: 'ETH',
      currencyCode: 'ETH',
      referenceSymbol: 'ETH',
      source: ethFallback.priceUsd ? 'iso4217-oracle' : 'unresolved',
    };
  }

  const spec = getCanonicalTokenByAddress(chainId, normalized);
  const feedPrice = await resolveEthUsdFeedPrice(chainId, normalized, spec?.symbol);
  if (feedPrice) {
    return feedPrice;
  }

  if (isChain138EthUsdPegToken(chainId, normalized, spec?.symbol)) {
    const native = await getChain138EthUsdOnChain();
    if (native?.priceUsd && native.priceUsd > 0) {
      return {
        priceUsd: native.priceUsd,
        symbol: spec?.symbol ?? 'WETH',
        currencyCode: spec?.currencyCode ?? 'ETH',
        referenceSymbol: 'ETH',
        source: 'native-oracle',
      };
    }
  }

  const marketData = await marketDataRepo.getMarketData(chainId, normalized);
  const canonical = resolveCanonicalPriceUsd(chainId, normalized);

  const priceUsd =
    marketData?.priceUsd != null && marketData.priceUsd > 0
      ? marketData.priceUsd
      : canonical.priceUsd;

  return {
    priceUsd,
    symbol: spec?.symbol,
    currencyCode: spec?.currencyCode,
    referenceSymbol: canonical.referenceSymbol,
    source:
      marketData?.priceUsd != null && marketData.priceUsd > 0
        ? 'market-data'
        : canonical.priceUsd != null
          ? 'iso4217-oracle'
          : 'unresolved',
  };
}
