import { Router, type Request, type Response } from 'express';
import { getCanonicalTokensByChain } from '../../config/canonical-tokens';
import { cacheMiddleware } from '../middleware/cache';
import { resolveIso4217SpotPrice } from '../../services/iso4217-spot-price';
import { refreshChain138LiveReferencePrices } from '../../services/chain138-onchain-reference-prices';

const router: Router = Router();

function parseChainId(value: unknown): number | null {
  const chainId = parseInt(String(value ?? ''), 10);
  return Number.isFinite(chainId) && chainId > 0 ? chainId : null;
}

function parseTokenAddresses(raw: unknown): string[] {
  const values = Array.isArray(raw)
    ? raw.flatMap((entry) => String(entry).split(','))
    : String(raw ?? '')
        .split(',')
        .map((entry) => entry.trim());

  const out: string[] = [];
  const seen = new Set<string>();
  for (const value of values) {
    const normalized = value.trim().toLowerCase();
    if (!/^0x[a-f0-9]{40}$/.test(normalized) || seen.has(normalized)) continue;
    seen.add(normalized);
    out.push(normalized);
    if (out.length >= 120) break;
  }
  return out;
}

function normalizeVsCurrency(raw: unknown): string {
  const value = String(raw ?? 'usd')
    .trim()
    .toLowerCase();
  return value || 'usd';
}

async function buildMetamaskSpotPriceMap(
  chainId: number,
  addresses: string[],
  vsCurrency: string,
): Promise<Record<string, Record<string, number>>> {
  await refreshChain138LiveReferencePrices(chainId);

  const response: Record<string, Record<string, number>> = {};

  await Promise.all(
    addresses.map(async (address) => {
      const spot = await resolveIso4217SpotPrice(chainId, address);
      if (spot.priceUsd != null && spot.priceUsd > 0) {
        response[address] = { [vsCurrency]: spot.priceUsd };
      }
    }),
  );

  return response;
}

/**
 * MetaMask Codefi Price API compatible spot prices for ISO-4217 c* tokens.
 * MetaMask extension/mobile will not consume this until chain 138 is registered on
 * price.api.cx.metamask.io — use this URL in Consensys / CoinGecko submission packets.
 */
router.get(
  '/v2/chains/:chainId/spot-prices',
  cacheMiddleware(60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const chainId = parseChainId(req.params.chainId);
      if (!chainId) {
        res.status(400).json({ message: 'Invalid chainId' });
        return;
      }

      const vsCurrency = normalizeVsCurrency(req.query.vsCurrency);
      const addresses = parseTokenAddresses(req.query.tokenAddresses);
      if (addresses.length === 0) {
        res.status(400).json({ message: 'tokenAddresses query parameter is required' });
        return;
      }

      const prices = await buildMetamaskSpotPriceMap(chainId, addresses, vsCurrency);
      res.json(prices);
    } catch {
      res.status(500).json({ message: 'Internal server error' });
    }
  },
);

router.get(
  '/v1/chains/:chainId/spot-prices/:tokenAddress',
  cacheMiddleware(60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const chainId = parseChainId(req.params.chainId);
      const address = String(req.params.tokenAddress || '').trim().toLowerCase();
      if (!chainId || !/^0x[a-f0-9]{40}$/.test(address)) {
        res.status(400).json({ message: 'Invalid chainId or tokenAddress' });
        return;
      }

      const vsCurrency = normalizeVsCurrency(req.query.vsCurrency);
      const spot = await resolveIso4217SpotPrice(chainId, address);
      if (spot.priceUsd == null || !(spot.priceUsd > 0)) {
        res.status(404).json({ message: 'Price unavailable' });
        return;
      }

      res.json({ [vsCurrency]: spot.priceUsd });
    } catch {
      res.status(500).json({ message: 'Internal server error' });
    }
  },
);

export default router;
