import { type CanonicalTokenSpec, getCanonicalTokenByAddress, getCanonicalTokenBySymbol } from '../config/canonical-tokens';

export interface CanonicalPriceResolution {
  priceUsd?: number;
  referenceSymbol?: string;
  source: 'env' | 'repo-fallback' | 'unresolved';
}

const FX_SNAPSHOT_GENERATED_AT = '2026-04-15';

// Repo-local inferred FX snapshot from scripts/lib/extraction_gap_closure.py.
const REPO_FALLBACK_PRICE_USD: Record<string, number> = {
  USD: 1,
  EUR: 1.1780,
  GBP: 1.3550353712543854,
  AUD: 0.7136366390016357,
  CAD: 0.7255928549430243,
  CHF: 1.2776572668112798,
  JPY: 0.006285683794888213,
  XAU: 5163.3401260328355,
  ETH: 2490,
  BTC: 90000,
  BNB: 610,
  POL: 0.78,
  AVAX: 48,
  CELO: 0.72,
  CRO: 0.14,
  XDAI: 1,
};

function normalizeAddress(value: string): string {
  return value.trim().toLowerCase();
}

function readEnvPrice(keys: string[]): number | undefined {
  for (const key of keys) {
    const raw = process.env[key];
    if (!raw || raw.trim() === '') continue;
    const parsed = Number(raw);
    if (Number.isFinite(parsed) && parsed > 0) {
      return parsed;
    }
  }
  return undefined;
}

function resolveReferenceSymbol(spec: CanonicalTokenSpec): string | undefined {
  const symbol = spec.symbol.toUpperCase();
  const currencyCode = String(spec.currencyCode || '').trim().toUpperCase();

  if (
    symbol === 'WETH' ||
    symbol === 'WETH9' ||
    symbol === 'WETH10' ||
    symbol === 'CETH' ||
    symbol === 'CETHL2' ||
    symbol === 'CWETH' ||
    symbol === 'CWETHL2'
  ) {
    return 'ETH';
  }

  if (symbol === 'CBTC' || symbol === 'CWBTC') {
    return 'BTC';
  }

  if (symbol === 'CXAUC' || symbol === 'CXAUT' || symbol === 'CAXAUC' || symbol === 'CAXAUT' || symbol === 'CWAXAUC' || symbol === 'CWAXAUT' || symbol === 'LIXAU') {
    return 'XAU';
  }

  if (currencyCode) {
    return currencyCode;
  }

  return undefined;
}

function resolvePegSourceSpec(spec: CanonicalTokenSpec, seenSymbols: Set<string> = new Set()): CanonicalTokenSpec {
  const symbol = spec.symbol.trim();
  const symbolUpper = symbol.toUpperCase();
  if (seenSymbols.has(symbolUpper)) {
    return spec;
  }

  const nextSeen = new Set(seenSymbols);
  nextSeen.add(symbolUpper);

  const familySymbol = String(spec.familySymbol || '').trim();
  if (familySymbol) {
    const familyMatch = getCanonicalTokenByAddressFromSymbolFamily(spec, familySymbol);
    if (familyMatch) {
      return resolvePegSourceSpec(familyMatch, nextSeen);
    }
  }

  const lendingWrapperMatch = /^(ac|vdc|sdc)(.+)$/i.exec(symbol);
  if (lendingWrapperMatch) {
    const underlyingSymbol = `c${lendingWrapperMatch[2]}`;
    const underlyingMatch = getCanonicalTokenByAddressFromSymbolFamily(spec, underlyingSymbol);
    if (underlyingMatch) {
      return resolvePegSourceSpec(underlyingMatch, nextSeen);
    }
  }

  return spec;
}

function getCanonicalTokenByAddressFromSymbolFamily(spec: CanonicalTokenSpec, symbol: string): CanonicalTokenSpec | undefined {
  for (const [chainIdText, address] of Object.entries(spec.addresses)) {
    if (!address || String(address).trim() === '') continue;
    const chainId = Number(chainIdText);
    if (!Number.isFinite(chainId)) continue;
    const familyMatch = getCanonicalTokenBySymbol(chainId, symbol);
    if (familyMatch) {
      return familyMatch;
    }
  }
  return undefined;
}

function resolveEnvPriceKeys(referenceSymbol: string): string[] {
  const symbol = referenceSymbol.toUpperCase();
  if (symbol === 'XAU') {
    return [
      'CHAIN138_CANONICAL_PRICE_USD_XAU',
      'CANONICAL_PRICE_USD_XAU',
      'XAU_SPOT_USD',
      'GOLD_USD_PRICE',
    ];
  }

  if (symbol === 'ETH') {
    return [
      'CHAIN138_CANONICAL_PRICE_USD_ETH',
      'CANONICAL_PRICE_USD_ETH',
      'ETH_PRICE_USD',
      'CHAIN138_D3_PILOT_WETH_USD',
    ];
  }

  return [
    `CHAIN138_CANONICAL_PRICE_USD_${symbol}`,
    `CANONICAL_PRICE_USD_${symbol}`,
    `${symbol}_PRICE_USD`,
  ];
}

export function resolveCanonicalPriceUsdForSpec(spec: CanonicalTokenSpec): CanonicalPriceResolution {
  const pegSourceSpec = resolvePegSourceSpec(spec);
  const referenceSymbol = resolveReferenceSymbol(pegSourceSpec);
  if (!referenceSymbol) {
    return { source: 'unresolved' };
  }

  const envPrice = readEnvPrice(resolveEnvPriceKeys(referenceSymbol));
  if (envPrice !== undefined) {
    return {
      priceUsd: envPrice,
      referenceSymbol,
      source: 'env',
    };
  }

  const fallback = REPO_FALLBACK_PRICE_USD[referenceSymbol];
  if (fallback !== undefined) {
    return {
      priceUsd: fallback,
      referenceSymbol,
      source: 'repo-fallback',
    };
  }

  return {
    referenceSymbol,
    source: 'unresolved',
  };
}

export function resolveCanonicalPriceUsd(chainId: number, address: string): CanonicalPriceResolution {
  const spec = getCanonicalTokenByAddress(chainId, normalizeAddress(address));
  if (!spec) {
    return { source: 'unresolved' };
  }
  return resolveCanonicalPriceUsdForSpec(spec);
}

export function getCanonicalPriceUsd(chainId: number, address: string): number | undefined {
  return resolveCanonicalPriceUsd(chainId, address).priceUsd;
}

export function getCanonicalPriceSnapshotGeneratedAt(): string {
  return FX_SNAPSHOT_GENERATED_AT;
}
