import {
  isGruV2CwMeshEdgeToken,
  pickExternalMarketDataForIndexer,
  resolveUsdValuation,
  mergeMarketWithValuation,
} from './valuation-precedence';

/** Bridged `cW*` on Ethereum; on Chain 138 the native form is `cUSDC` (`c*`), not `cW*`. */
const ETH_MAINNET_CWUSDC = '0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a';

describe('valuation-precedence', () => {
  const prevIndexerAge = process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS;
  const prevEdgeCwDexFirst = process.env.TOKEN_AGG_EDGE_CW_DEX_FIRST;

  afterEach(() => {
    if (prevIndexerAge === undefined) {
      delete process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS;
    } else {
      process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS = prevIndexerAge;
    }
    if (prevEdgeCwDexFirst === undefined) {
      delete process.env.TOKEN_AGG_EDGE_CW_DEX_FIRST;
    } else {
      process.env.TOKEN_AGG_EDGE_CW_DEX_FIRST = prevEdgeCwDexFirst;
    }
  });

  it('prefers fresh indexer price over canonical', () => {
    process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS = '3600';
    const lu = new Date(Date.now() - 60_000);
    const v = resolveUsdValuation({
      chainId: 1,
      normalizedAddress: '0x0000000000000000000000000000000000000001',
      indexer: {
        chainId: 1,
        tokenAddress: '0x0000000000000000000000000000000000000001',
        priceUsd: 2.5,
        volume24h: 0,
        volume7d: 0,
        volume30d: 0,
        liquidityUsd: 0,
        holdersCount: 0,
        transfers24h: 0,
        lastUpdated: lu,
      },
      coingecko: { priceUsd: 9.99, lastUpdated: new Date() },
    });
    expect(v.sourceLayer).toBe('indexer_market');
    expect(v.priceUsd).toBe(2.5);
    expect(v.stale).toBe(false);
  });

  it('skips stale indexer and uses external when indexer is too old', () => {
    process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS = '60';
    const lu = new Date(Date.now() - 120_000);
    const v = resolveUsdValuation({
      chainId: 1,
      normalizedAddress: '0x0000000000000000000000000000000000000001',
      indexer: {
        chainId: 1,
        tokenAddress: '0x0000000000000000000000000000000000000001',
        priceUsd: 2.5,
        volume24h: 0,
        volume7d: 0,
        volume30d: 0,
        liquidityUsd: 0,
        holdersCount: 0,
        transfers24h: 0,
        lastUpdated: lu,
      },
      coingecko: { priceUsd: 9.99, lastUpdated: new Date() },
    });
    expect(v.sourceLayer).toBe('external_coingecko');
    expect(v.priceUsd).toBe(9.99);
    expect(v.stale).toBe(false);
  });

  it('falls back to stale indexer when nothing else is available', () => {
    process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS = '60';
    const lu = new Date(Date.now() - 120_000);
    const v = resolveUsdValuation({
      chainId: 1,
      normalizedAddress: '0x0000000000000000000000000000000000000001',
      indexer: {
        chainId: 1,
        tokenAddress: '0x0000000000000000000000000000000000000001',
        priceUsd: 2.5,
        volume24h: 0,
        volume7d: 0,
        volume30d: 0,
        liquidityUsd: 0,
        holdersCount: 0,
        transfers24h: 0,
        lastUpdated: lu,
      },
    });
    expect(v.sourceLayer).toBe('indexer_market');
    expect(v.priceUsd).toBe(2.5);
    expect(v.stale).toBe(true);
  });

  it('detects GRU v2 cW* on edge chains via canonical registry', () => {
    expect(isGruV2CwMeshEdgeToken(1, ETH_MAINNET_CWUSDC)).toBe(true);
    expect(isGruV2CwMeshEdgeToken(138, ETH_MAINNET_CWUSDC)).toBe(false);
    expect(isGruV2CwMeshEdgeToken(1, '0x0000000000000000000000000000000000000001')).toBe(false);
  });

  it('for edge GRU v2 cW*, prefers DexScreener over CoinGecko when indexer is stale', () => {
    process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS = '60';
    const lu = new Date(Date.now() - 120_000);
    const v = resolveUsdValuation({
      chainId: 1,
      normalizedAddress: ETH_MAINNET_CWUSDC,
      indexer: {
        chainId: 1,
        tokenAddress: ETH_MAINNET_CWUSDC,
        priceUsd: 2.5,
        volume24h: 0,
        volume7d: 0,
        volume30d: 0,
        liquidityUsd: 0,
        holdersCount: 0,
        transfers24h: 0,
        lastUpdated: lu,
      },
      coingecko: { priceUsd: 9.99, lastUpdated: new Date() },
      dexscreener: { priceUsd: 1.0, lastUpdated: new Date() },
    });
    expect(v.gruV2CwEdgeDexPriority).toBe(true);
    expect(v.sourceLayer).toBe('external_dexscreener');
    expect(v.priceUsd).toBe(1.0);
    expect(v.precedenceRank).toBe(2);
  });

  it('pickExternalMarketDataForIndexer prefers DexScreener for edge cW*', () => {
    const dex = { priceUsd: 1, lastUpdated: new Date() };
    const cg = { priceUsd: 2, lastUpdated: new Date() };
    const picked = pickExternalMarketDataForIndexer(1, ETH_MAINNET_CWUSDC, {
      coingecko: cg,
      cmc: null,
      dexscreener: dex,
    });
    expect(picked?.priceUsd).toBe(1);
  });

  it('pickExternalMarketDataForIndexer prefers CoinGecko when edge cW* flag is off', () => {
    process.env.TOKEN_AGG_EDGE_CW_DEX_FIRST = '0';
    const dex = { priceUsd: 1, lastUpdated: new Date() };
    const cg = { priceUsd: 2, lastUpdated: new Date() };
    const picked = pickExternalMarketDataForIndexer(1, ETH_MAINNET_CWUSDC, {
      coingecko: cg,
      cmc: null,
      dexscreener: dex,
    });
    expect(picked?.priceUsd).toBe(2);
  });

  it('TOKEN_AGG_EDGE_CW_DEX_FIRST=0 restores CoinGecko-before-DexScreener for edge cW*', () => {
    process.env.TOKEN_AGG_EDGE_CW_DEX_FIRST = '0';
    process.env.TOKEN_AGG_PRICE_INDEXER_MAX_AGE_SECONDS = '60';
    const lu = new Date(Date.now() - 120_000);
    const v = resolveUsdValuation({
      chainId: 1,
      normalizedAddress: ETH_MAINNET_CWUSDC,
      indexer: {
        chainId: 1,
        tokenAddress: ETH_MAINNET_CWUSDC,
        priceUsd: 2.5,
        volume24h: 0,
        volume7d: 0,
        volume30d: 0,
        liquidityUsd: 0,
        holdersCount: 0,
        transfers24h: 0,
        lastUpdated: lu,
      },
      coingecko: { priceUsd: 9.99, lastUpdated: new Date() },
      dexscreener: { priceUsd: 1.0, lastUpdated: new Date() },
    });
    expect(v.gruV2CwEdgeDexPriority).toBe(false);
    expect(v.sourceLayer).toBe('external_coingecko');
    expect(v.priceUsd).toBe(9.99);
  });

  it('mergeMarketWithValuation applies priced layer onto indexer row', () => {
    const pricing = resolveUsdValuation({
      chainId: 138,
      normalizedAddress: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'.toLowerCase(),
      indexer: null,
      coingecko: undefined,
    });
    const m = mergeMarketWithValuation(138, '0xc02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', null, pricing);
    expect(m?.priceUsd).toBe(pricing.priceUsd);
  });
});
