/**
 * CMC and CoinGecko reporting API: all tokens, liquidity, volume, and reportable data.
 * Use for listing submissions and external aggregator sync.
 */

import { Router, Request, Response } from 'express';
import { TokenRepository } from '../../database/repositories/token-repo';
import { MarketDataRepository } from '../../database/repositories/market-data-repo';
import { PoolRepository } from '../../database/repositories/pool-repo';
import {
  CANONICAL_TOKENS,
  getCanonicalTokensByChain,
  getLogoUriForSpec,
  getTokenRegistryFamily,
} from '../../config/canonical-tokens';
import { resolvePoolTokenDisplays } from '../../services/token-display';
import { getSupportedChainIds } from '../../config/chains';
import { cacheMiddleware } from '../middleware/cache';
import { fetchRemoteJson } from '../utils/fetch-remote-json';
import { buildCrossChainReport } from '../../indexer/cross-chain-indexer';
import { logger } from '../../utils/logger';
import { filterPoolsForExposure, getActiveTransportPairs, getGruTransportMetadata } from '../../config/gru-transport';
import {
  buildCwRegistryChains,
  buildGasRegistryChains,
  loadDeploymentStatusFile,
  type CwRegistryChain,
} from '../../config/deployment-status';
import { getGruV2DeploymentPoolRows } from '../../config/gru-v2-deployment-pools';
import { getCanonicalPriceSnapshotGeneratedAt, getCanonicalPriceUsd } from '../../services/canonical-price-oracle';

const router: Router = Router();
const tokenRepo = new TokenRepository();
const marketDataRepo = new MarketDataRepository();
const poolRepo = new PoolRepository();

/** Build token entries with DB market/pool data for a chain */
async function buildTokenReport(chainId: number) {
  const canonical = getCanonicalTokensByChain(chainId);
  const out: Array<{
    chainId: number;
    address: string;
    symbol: string;
    name: string;
    type: string;
    decimals: number;
    currencyCode?: string;
    registryFamily?: string;
    familySymbol?: string;
    deploymentVersion?: string;
    deploymentStatus?: string;
    preferredForX402?: boolean;
    liquiditySourceSymbol?: string;
    market?: {
      priceUsd?: number;
      volume24h: number;
      volume7d: number;
      volume30d: number;
      marketCapUsd?: number;
      liquidityUsd: number;
      lastUpdated: string;
    };
    pools: Array<{
      poolAddress: string;
      dex: string;
      token0: string;
      token1: string;
      tvl: number;
      volume24h: number;
    }>;
    fromDb: boolean;
  }> = [];

  for (const spec of canonical) {
    const address = spec.addresses[chainId];
    if (!address || String(address).trim() === '') continue;

    const [dbToken, marketData, pools] = await Promise.all([
      tokenRepo.getToken(chainId, address),
      marketDataRepo.getMarketData(chainId, address),
      poolRepo.getPoolsByToken(chainId, address),
    ]);
    const exposedPools = filterPoolsForExposure(chainId, pools);

    const resolvedPools = await Promise.all(
      exposedPools.map(async (p) => {
        const { token0, token1 } = await resolvePoolTokenDisplays(tokenRepo, chainId, p.token0Address, p.token1Address);
        return {
          poolAddress: p.poolAddress,
          dex: p.dexType,
          token0,
          token1,
          tvl: p.totalLiquidityUsd,
          volume24h: p.volume24h,
        };
      })
    );

    const fallbackPriceUsd = getCanonicalPriceUsd(chainId, address);

    out.push({
      chainId,
      address: address.toLowerCase(),
      symbol: spec.symbol,
      name: dbToken?.name ?? spec.name,
      type: spec.type,
      decimals: spec.decimals,
      currencyCode: spec.currencyCode,
      registryFamily: getTokenRegistryFamily(spec),
      familySymbol: spec.familySymbol,
      deploymentVersion: spec.deploymentVersion,
      deploymentStatus: spec.deploymentStatus,
      preferredForX402: spec.preferredForX402,
      liquiditySourceSymbol: spec.liquiditySourceSymbol,
      market: marketData
        ? {
            priceUsd: marketData.priceUsd ?? fallbackPriceUsd,
            volume24h: marketData.volume24h,
            volume7d: marketData.volume7d,
            volume30d: marketData.volume30d,
            marketCapUsd: marketData.marketCapUsd,
            liquidityUsd: marketData.liquidityUsd,
            lastUpdated: marketData.lastUpdated?.toISOString() ?? '',
          }
        : fallbackPriceUsd !== undefined
          ? {
              priceUsd: fallbackPriceUsd,
              volume24h: 0,
              volume7d: 0,
              volume30d: 0,
              liquidityUsd: 0,
              lastUpdated: `${getCanonicalPriceSnapshotGeneratedAt()}T00:00:00.000Z`,
            }
        : undefined,
      pools: resolvedPools.map((p) => ({
        poolAddress: p.poolAddress,
        dex: p.dex,
        token0: p.token0.address,
        token1: p.token1.address,
        token0Symbol: p.token0.symbol,
        token1Symbol: p.token1.symbol,
        tvl: p.tvl,
        volume24h: p.volume24h,
      })),
      fromDb: !!dbToken,
    });
  }

  return out;
}

function describeToken(spec: { currencyCode?: string; registryFamily?: string }): string | undefined {
  const family = String(spec.registryFamily || '').trim();
  const code = String(spec.currencyCode || '').trim().toUpperCase();
  if (!code) return undefined;
  if (family === 'gas_native') {
    return `Governance-approved gas-native ${code} compliant token`;
  }
  if (family === 'monetary_unit') {
    return `GRU monetary-unit ${code} compliant token`;
  }
  if (family === 'commodity') {
    return `Governance-approved commodity ${code} compliant token`;
  }
  return `ISO-4217 ${code} compliant token`;
}

function buildGruTransportOverview() {
  const gruTransportMetadata = getGruTransportMetadata();
  if (!gruTransportMetadata) return undefined;

  return {
    system: gruTransportMetadata.system,
    summary: gruTransportMetadata.counts,
    gasAssetFamilies: gruTransportMetadata.gasAssetFamilies ?? [],
    gasRedeemGroups: gruTransportMetadata.gasRedeemGroups ?? [],
    gasProtocolExposure: gruTransportMetadata.gasProtocolExposure ?? [],
    activeTransportPairs: getActiveTransportPairs().map((pair) => ({
      key: pair.key,
      canonicalSymbol: pair.canonicalSymbol,
      mirroredSymbol: pair.mirroredSymbol,
      destinationChainId: pair.destinationChainId,
      destinationChainName: pair.destinationChainName ?? null,
      assetClass: pair.assetClass,
      familyKey: pair.familyKey,
      backingMode: pair.backingMode,
      redeemPolicy: pair.redeemPolicy,
      wrappedNativeQuoteSymbol: pair.wrappedNativeQuoteSymbol ?? null,
      stableQuoteSymbol: pair.stableQuoteSymbol ?? null,
      eligible: pair.eligible === true,
      runtimeReady: pair.runtimeReady === true,
      supplyInvariantSatisfied: pair.supplyInvariantSatisfied ?? null,
      eligibilityBlockers: Array.isArray(pair.eligibilityBlockers)
        ? pair.eligibilityBlockers
        : [],
      runtimeMissingRequirements: Array.isArray(pair.runtimeMissingRequirements)
        ? pair.runtimeMissingRequirements
        : [],
    })),
  };
}

function buildCanonicalCwFallback(chainIdFilter?: number | null): CwRegistryChain[] {
  const grouped = new Map<number, CwRegistryChain>();

  for (const spec of CANONICAL_TOKENS) {
    if (spec.type !== 'w') continue;
    for (const [chainIdText, address] of Object.entries(spec.addresses)) {
      const chainId = Number(chainIdText);
      if (!address || Number.isNaN(chainId)) continue;
      if (chainIdFilter && chainId !== chainIdFilter) continue;

      const existing = grouped.get(chainId) ?? {
        chainId,
        chainIdText,
        name: `Chain ${chainIdText}`,
        tokens: [],
      };

      existing.tokens.push({ symbol: spec.symbol, address });
      grouped.set(chainId, existing);
    }
  }

  return Array.from(grouped.values())
    .map((row) => ({
      ...row,
      tokens: row.tokens.sort((a, b) => a.symbol.localeCompare(b.symbol)),
    }))
    .sort((a, b) => a.chainId - b.chainId);
}

/** GET /report/cross-chain — cross-chain pools, bridge volume, atomic swaps (Chain 138, ALL Mainnet) */
router.get(
  '/cross-chain',
  cacheMiddleware(2 * 60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const chainId = parseInt(req.query.chainId as string, 10) || 138;
      const report = await buildCrossChainReport(chainId);
      res.json({
        ...report,
        format: 'cross-chain-report',
        documentation: 'Use for CMC/CoinGecko submission alongside single-chain reports. Includes CCIP, Alltra, Trustless bridge events and volume by lane.',
      });
    } catch (error) {
      logger.error('Error building report/cross-chain:', error);
      res.status(500).json({
        error: 'Internal server error',
        crossChainPools: [],
        volumeByLane: [],
        atomicSwapVolume24h: 0,
        bridgeVolume24hTotal: 0,
        events: [],
      });
    }
  }
);

/** GET /report/all — all tokens, pools, liquidity, volume (unified) + cross-chain */
router.get(
  '/all',
  cacheMiddleware(2 * 60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const chainIdParam = req.query.chainId as string | undefined;
      const chainIds = chainIdParam
        ? [parseInt(chainIdParam, 10)].filter((n) => !isNaN(n))
        : getSupportedChainIds();

      const tokensByChain: Record<number, Awaited<ReturnType<typeof buildTokenReport>>> = {};
      const poolsByChain: Record<number, Awaited<ReturnType<typeof poolRepo.getPoolsByChain>>> = {};

      for (const chainId of chainIds) {
        tokensByChain[chainId] = await buildTokenReport(chainId);
        poolsByChain[chainId] = filterPoolsForExposure(chainId, await poolRepo.getPoolsByChain(chainId));
      }

      const crossChainReport = await buildCrossChainReport(138).catch(() => null);
      const gruTransport = buildGruTransportOverview();

      const totalLiquidityByChain: Record<number, number> = {};
      const totalVolume24hByChain: Record<number, number> = {};
      for (const chainId of chainIds) {
        const pools = poolsByChain[chainId] || [];
        totalLiquidityByChain[chainId] = pools.reduce((s, p) => s + (p.totalLiquidityUsd || 0), 0);
        totalVolume24hByChain[chainId] = pools.reduce((s, p) => s + (p.volume24h || 0), 0);
      }

      res.json({
        generatedAt: new Date().toISOString(),
        chains: chainIds,
        tokens: tokensByChain,
        pools: poolsByChain,
        summary: {
          totalLiquidityUsdByChain: totalLiquidityByChain,
          totalVolume24hUsdByChain: totalVolume24hByChain,
          tokenCountByChain: Object.fromEntries(
            chainIds.map((c) => [c, (tokensByChain[c] || []).length])
          ),
          poolCountByChain: Object.fromEntries(
            chainIds.map((c) => [c, (poolsByChain[c] || []).length])
          ),
          crossChainBridgeVolume24h: crossChainReport?.bridgeVolume24hTotal,
          crossChainAtomicSwapVolume24h: crossChainReport?.atomicSwapVolume24h,
        },
        crossChain: crossChainReport
          ? {
              crossChainPools: crossChainReport.crossChainPools,
              volumeByLane: crossChainReport.volumeByLane,
              atomicSwapVolume24h: crossChainReport.atomicSwapVolume24h,
              bridgeVolume24hTotal: crossChainReport.bridgeVolume24hTotal,
            }
          : undefined,
        gruTransport,
      });
    } catch (error) {
      logger.error('Error building report/all:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

/** GET /report/coingecko — format suitable for CoinGecko submission / API */
router.get(
  '/coingecko',
  cacheMiddleware(2 * 60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const chainId = parseInt(req.query.chainId as string, 10) || 138;
      const tokens = await buildTokenReport(chainId);

      const coingeckoFormat = tokens.map((t) => ({
        chain_id: chainId,
        contract_address: t.address,
        id: `${t.symbol.toLowerCase()}-${chainId}`,
        symbol: t.symbol,
        name: t.name,
        asset_platform_id: chainId === 138 ? 'defi-oracle-meta' : chainId === 651940 ? 'all-mainnet' : `chain-${chainId}`,
        decimals: t.decimals,
        description: describeToken(t),
        market_data: t.market
          ? {
              current_price: { usd: t.market.priceUsd },
              total_volume: t.market.volume24h,
              market_cap: t.market.marketCapUsd,
              liquidity_usd: t.market.liquidityUsd,
              last_updated: t.market.lastUpdated,
            }
          : undefined,
        liquidity_pools: t.pools.map((p) => ({
          pool_address: p.poolAddress,
          dex_id: p.dex,
          tvl_usd: p.tvl,
          volume_24h_usd: p.volume24h,
        })),
      }));

      const crossChainReport = await buildCrossChainReport(chainId).catch(() => null);

      res.json({
        generatedAt: new Date().toISOString(),
        chainId,
        format: 'coingecko-submission',
        tokens: coingeckoFormat,
        crossChain: crossChainReport
          ? {
              crossChainPools: crossChainReport.crossChainPools,
              volumeByLane: crossChainReport.volumeByLane,
              atomicSwapVolume24h: crossChainReport.atomicSwapVolume24h,
              bridgeVolume24hTotal: crossChainReport.bridgeVolume24hTotal,
            }
          : undefined,
        documentation: 'https://www.coingecko.com/en/api/documentation',
      });
    } catch (error) {
      logger.error('Error building report/coingecko:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

/** GET /report/cmc — format suitable for CoinMarketCap submission / API */
router.get(
  '/cmc',
  cacheMiddleware(2 * 60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const chainId = parseInt(req.query.chainId as string, 10) || 138;
      const tokens = await buildTokenReport(chainId);

      const cmcFormat = tokens.map((t) => ({
        chain_id: chainId,
        contract_address: t.address,
        symbol: t.symbol,
        name: t.name,
        decimals: t.decimals,
        volume_24h: t.market?.volume24h,
        market_cap: t.market?.marketCapUsd,
        liquidity_usd: t.market?.liquidityUsd ?? t.pools.reduce((s, p) => s + p.tvl, 0),
        pairs: t.pools.map((p) => ({
          pair_address: p.poolAddress,
          dex_id: p.dex,
          base: t.address,
          quote: p.token0 === t.address ? p.token1 : p.token0,
          liquidity_usd: p.tvl,
          volume_24h_usd: p.volume24h,
        })),
      }));

      const crossChainReport = await buildCrossChainReport(chainId).catch(() => null);

      res.json({
        generatedAt: new Date().toISOString(),
        chainId,
        format: 'coinmarketcap-dex',
        tokens: cmcFormat,
        crossChain: crossChainReport
          ? {
              crossChainPools: crossChainReport.crossChainPools,
              volumeByLane: crossChainReport.volumeByLane,
              atomicSwapVolume24h: crossChainReport.atomicSwapVolume24h,
              bridgeVolume24hTotal: crossChainReport.bridgeVolume24hTotal,
            }
          : undefined,
        documentation: 'https://coinmarketcap.com/api/documentation',
      });
    } catch (error) {
      logger.error('Error building report/cmc:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

/** GET /report/token-list — flat list of all canonical tokens (Uniswap token list format with logoURI).
 * If TOKEN_LIST_JSON_URL is set (e.g. GitHub raw URL), fetches and returns that JSON; optional ?chainId= filters tokens.
 */
router.get(
  '/token-list',
  cacheMiddleware(5 * 60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const tokenListUrl = process.env.TOKEN_LIST_JSON_URL?.trim();
      if (tokenListUrl) {
        try {
          const data = (await fetchRemoteJson(tokenListUrl)) as {
            name?: string;
            version?: string;
            timestamp?: string;
            logoURI?: string;
            tokens?: Array<{ chainId?: number; address?: string; symbol?: string; name?: string; decimals?: number; [key: string]: unknown }>;
          };
          const chainIdParam = req.query.chainId as string | undefined;
          const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
          let tokens = Array.isArray(data.tokens) ? data.tokens : [];
          if (!isNaN(chainIdFilter as number)) {
            tokens = tokens.filter((t) => t.chainId === chainIdFilter);
          }
          return res.json({
            name: data.name ?? 'Token List',
            version: data.version ?? '1.0.0',
            timestamp: data.timestamp ?? new Date().toISOString(),
            logoURI: data.logoURI,
            tokens,
          });
        } catch (err) {
          logger.error('TOKEN_LIST_JSON_URL fetch failed, using built-in token list:', err);
        }
      }

      const chainIdParam = req.query.chainId as string | undefined;
      const chainIds = chainIdParam
        ? [parseInt(chainIdParam, 10)].filter((n) => !isNaN(n))
        : getSupportedChainIds();

      const list: Array<{
        chainId: number;
        address: string;
        symbol: string;
        name: string;
        decimals: number;
        type: string;
        logoURI: string;
        registryFamily?: string;
        familySymbol?: string;
        deploymentVersion?: string;
        deploymentStatus?: string;
        preferredForX402?: boolean;
      }> = [];

      for (const chainId of chainIds) {
        const specs = getCanonicalTokensByChain(chainId);
        for (const spec of specs) {
          const address = spec.addresses[chainId];
          if (address) {
            list.push({
              chainId,
              address: address.toLowerCase(),
              symbol: spec.symbol,
              name: spec.name,
              decimals: spec.decimals,
              type: spec.type,
              logoURI: getLogoUriForSpec(spec),
              registryFamily: getTokenRegistryFamily(spec),
              familySymbol: spec.familySymbol,
              deploymentVersion: spec.deploymentVersion,
              deploymentStatus: spec.deploymentStatus,
              preferredForX402: spec.preferredForX402,
            });
          }
        }
      }

      res.json({
        name: 'GRU Canonical Token List',
        version: '1.0.0',
        timestamp: new Date().toISOString(),
        logoURI: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png',
        tokens: list,
      });
    } catch (error) {
      logger.error('Error building report/token-list:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

/** GET /report/cw-registry — live cW* registry from deployment-status.json when available. */
router.get('/cw-registry', async (req: Request, res: Response) => {
  try {
    const chainIdParam = req.query.chainId as string | undefined;
    const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
    const fileBackedRegistry = loadDeploymentStatusFile();

    let chains = fileBackedRegistry
      ? buildCwRegistryChains(fileBackedRegistry.data)
      : buildCanonicalCwFallback(chainIdFilter);

    if (chainIdFilter && !Number.isNaN(chainIdFilter)) {
      chains = chains.filter((row) => row.chainId === chainIdFilter);
    }

    res.set('Cache-Control', 'public, max-age=0, must-revalidate');
    res.json({
      generatedAt: new Date().toISOString(),
      source: fileBackedRegistry ? 'deployment-status-file' : 'canonical-fallback',
      complete: !!fileBackedRegistry,
      version: fileBackedRegistry?.data.version,
      updated: fileBackedRegistry?.data.updated,
      lastModified: fileBackedRegistry?.lastModified,
      chains,
    });
  } catch (error) {
    logger.error('Error building report/cw-registry:', error);
    res.status(500).json({ error: 'Internal server error', chains: [] });
  }
});

/** GET /report/gru-v2-pmm-pools — all GRU v2 PMM pools from deployment-status (stable, volatile, gas) with resolved token addresses. */
router.get('/gru-v2-pmm-pools', async (req: Request, res: Response) => {
  try {
    const chainIdParam = req.query.chainId as string | undefined;
    const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
    const fileBacked = loadDeploymentStatusFile();
    let pools = getGruV2DeploymentPoolRows();

    if (chainIdFilter && !Number.isNaN(chainIdFilter)) {
      pools = pools.filter((p) => p.chainId === chainIdFilter);
    }

    res.set('Cache-Control', 'public, max-age=0, must-revalidate');
    res.json({
      generatedAt: new Date().toISOString(),
      source: fileBacked ? 'deployment-status-file' : 'none',
      complete: !!fileBacked,
      version: fileBacked?.data.version,
      updated: fileBacked?.data.updated,
      lastModified: fileBacked?.lastModified,
      homeChainId: fileBacked?.data.homeChainId,
      count: pools.length,
      pools,
    });
  } catch (error) {
    logger.error('Error building report/gru-v2-pmm-pools:', error);
    res.status(500).json({ error: 'Internal server error', pools: [] });
  }
});

/** GET /report/gas-registry — live gas-family rollout registry from deployment-status.json plus GRU transport metadata. */
router.get('/gas-registry', async (req: Request, res: Response) => {
  try {
    const chainIdParam = req.query.chainId as string | undefined;
    const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
    const fileBackedRegistry = loadDeploymentStatusFile();
    const gruTransport = buildGruTransportOverview();
    const runtimeGasPairs = getActiveTransportPairs()
      .filter((pair) => pair.assetClass === 'gas_native')
      .map((pair) => ({
        key: pair.key,
        destinationChainId: pair.destinationChainId,
        destinationChainName: pair.destinationChainName ?? null,
        familyKey: pair.familyKey ?? null,
        canonicalSymbol: pair.canonicalSymbol,
        mirroredSymbol: pair.mirroredSymbol,
        wrappedNativeQuoteSymbol: pair.wrappedNativeQuoteSymbol ?? null,
        stableQuoteSymbol: pair.stableQuoteSymbol ?? null,
        backingMode: pair.backingMode ?? null,
        redeemPolicy: pair.redeemPolicy ?? null,
        runtimeReady: pair.runtimeReady === true,
        supplyInvariantSatisfied: pair.supplyInvariantSatisfied ?? null,
        eligibilityBlockers: Array.isArray(pair.eligibilityBlockers) ? pair.eligibilityBlockers : [],
        runtimeMissingRequirements: Array.isArray(pair.runtimeMissingRequirements)
          ? pair.runtimeMissingRequirements
          : [],
      }));

    let chains = fileBackedRegistry ? buildGasRegistryChains(fileBackedRegistry.data) : [];
    if (chainIdFilter && !Number.isNaN(chainIdFilter)) {
      chains = chains.filter((row) => row.chainId === chainIdFilter);
    }

    res.set('Cache-Control', 'public, max-age=0, must-revalidate');
    res.json({
      generatedAt: new Date().toISOString(),
      source: fileBackedRegistry ? 'deployment-status-file' : 'transport-config-only',
      complete: !!fileBackedRegistry,
      version: fileBackedRegistry?.data.version,
      updated: fileBackedRegistry?.data.updated,
      lastModified: fileBackedRegistry?.lastModified,
      gasAssetFamilies: gruTransport?.gasAssetFamilies ?? [],
      gasRedeemGroups: gruTransport?.gasRedeemGroups ?? [],
      gasProtocolExposure: gruTransport?.gasProtocolExposure ?? [],
      runtimePairs: runtimeGasPairs,
      chains,
    });
  } catch (error) {
    logger.error('Error building report/gas-registry:', error);
    res.status(500).json({ error: 'Internal server error', chains: [] });
  }
});

/** GET /report/canonical — raw canonical spec list (no DB merge) */
router.get(
  '/canonical',
  cacheMiddleware(10 * 60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const gruTransport = buildGruTransportOverview();
      res.json({
        generatedAt: new Date().toISOString(),
        gruTransport,
        tokens: CANONICAL_TOKENS.map((t) => ({
          symbol: t.symbol,
          name: t.name,
          type: t.type,
          decimals: t.decimals,
          currencyCode: t.currencyCode,
          registryFamily: getTokenRegistryFamily(t),
          familySymbol: t.familySymbol,
          deploymentVersion: t.deploymentVersion,
          deploymentStatus: t.deploymentStatus,
          preferredForX402: t.preferredForX402,
          liquiditySourceSymbol: t.liquiditySourceSymbol,
          addresses: t.addresses,
        })),
      });
    } catch (error) {
      logger.error('Error building report/canonical:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

export default router;
