/**
 * Token mapping API: exposes config/token-mapping-loader (multichain) when run from monorepo.
 * GET /api/v1/token-mapping?fromChain=138&toChain=651940
 * GET /api/v1/token-mapping/pairs
 */

import { Router, Request, Response } from 'express';
import { cacheMiddleware } from '../middleware/cache';
import { loadTokenMappingLoader } from '../../config/repo-config-loader';

const router: Router = Router();

function loadMultichainLoader(): {
  getTokenMappingForPair: (from: number, to: number) => { tokens: unknown[]; addressMapFromTo: Record<string, string>; addressMapToFrom: Record<string, string> } | null;
  getAllMultichainPairs: () => Array<{ fromChainId: number; toChainId: number; notes?: string }>;
  getMappedAddress: (from: number, to: number, addr: string) => string | undefined;
  getGruTransportMetadata?: () => {
    system: Record<string, unknown> | null;
    terminology: Record<string, string>;
    enabledCanonicalTokens: Array<Record<string, unknown>>;
    enabledDestinationChains: Array<Record<string, unknown>>;
    counts: Record<string, number>;
  } | null;
  getActiveTransportPairs?: () => Array<Record<string, unknown>>;
  getActiveTransportPair?: (from: number, to: number, criteria?: Record<string, unknown>) => Record<string, unknown> | null;
  getActivePublicPools?: () => Array<Record<string, unknown>>;
  getEnabledCanonicalToken?: (identifier: string) => Record<string, unknown> | null;
  isGasRedemptionPathAllowed?: (from: number, to: number, identifier: string) => boolean;
} | null {
  const loader = loadTokenMappingLoader<{
    getTokenMappingForPair: (from: number, to: number) => { tokens: unknown[]; addressMapFromTo: Record<string, string>; addressMapToFrom: Record<string, string> } | null;
    getAllMultichainPairs: () => Array<{ fromChainId: number; toChainId: number; notes?: string }>;
    getMappedAddress: (from: number, to: number, addr: string) => string | undefined;
    getGruTransportMetadata?: () => {
      system: Record<string, unknown> | null;
      terminology: Record<string, string>;
      enabledCanonicalTokens: Array<Record<string, unknown>>;
      enabledDestinationChains: Array<Record<string, unknown>>;
      counts: Record<string, number>;
    } | null;
    getActiveTransportPairs?: () => Array<Record<string, unknown>>;
    getActiveTransportPair?: (from: number, to: number, criteria?: Record<string, unknown>) => Record<string, unknown> | null;
    getActivePublicPools?: () => Array<Record<string, unknown>>;
    getEnabledCanonicalToken?: (identifier: string) => Record<string, unknown> | null;
    isGasRedemptionPathAllowed?: (from: number, to: number, identifier: string) => boolean;
  }>();
  if (loader) {
    return loader;
  }
  return null;
}

/**
 * GET /api/v1/token-mapping?fromChain=138&toChain=651940
 * Returns token mapping for a chain pair (from config/token-mapping-multichain.json).
 */
router.get(
  '/',
  cacheMiddleware(5 * 60 * 1000),
  (req: Request, res: Response) => {
    const fromChain = parseInt(String(req.query.fromChain ?? req.query.from), 10);
    const toChain = parseInt(String(req.query.toChain ?? req.query.to), 10);
    if (!Number.isFinite(fromChain) || !Number.isFinite(toChain)) {
      return res.status(400).json({
        error: 'Missing or invalid fromChain and toChain query params',
        example: '/api/v1/token-mapping?fromChain=138&toChain=651940',
      });
    }

    const loader = loadMultichainLoader();
    if (!loader) {
      return res.status(503).json({
        error: 'Token mapping config not available (run from monorepo with config/token-mapping-multichain.json)',
      });
    }

    const result = loader.getTokenMappingForPair(fromChain, toChain);
    if (!result) {
      return res.status(404).json({
        error: 'No token mapping for this chain pair',
        fromChain,
        toChain,
      });
    }

    const activePairs = loader.getActiveTransportPairs
      ? loader
          .getActiveTransportPairs()
          .filter((pair) => {
            const canonicalChainId = Number(pair.canonicalChainId);
            const destinationChainId = Number(pair.destinationChainId);
            return (
              (canonicalChainId === fromChain && destinationChainId === toChain) ||
              (canonicalChainId === toChain && destinationChainId === fromChain)
            );
          })
      : [];

    return res.json({
      fromChainId: fromChain,
      toChainId: toChain,
      tokens: result.tokens,
      addressMapFromTo: result.addressMapFromTo,
      addressMapToFrom: result.addressMapToFrom,
      gruTransport: loader.getGruTransportMetadata
        ? {
            system: loader.getGruTransportMetadata()?.system ?? null,
            activePairs,
          }
        : undefined,
    });
  }
);

/**
 * GET /api/v1/token-mapping/pairs
 * Returns all defined chain pairs.
 */
router.get(
  '/pairs',
  cacheMiddleware(5 * 60 * 1000),
  (req: Request, res: Response) => {
    const loader = loadMultichainLoader();
    if (!loader) {
      return res.status(503).json({
        error: 'Token mapping config not available (run from monorepo)',
      });
    }
    const pairs = loader.getAllMultichainPairs();
    return res.json({
      pairs,
      gruTransport: loader.getGruTransportMetadata
        ? {
            system: loader.getGruTransportMetadata()?.system ?? null,
            activePairs: loader.getActiveTransportPairs ? loader.getActiveTransportPairs() : [],
          }
        : undefined,
    });
  }
);

/**
 * GET /api/v1/token-mapping/transport/active
 * Returns the GRU Monetary Transport Layer overlay as seen by token-aggregation.
 */
router.get(
  '/transport/active',
  cacheMiddleware(5 * 60 * 1000),
  (_req: Request, res: Response) => {
    const loader = loadMultichainLoader();
    if (!loader || !loader.getGruTransportMetadata || !loader.getActiveTransportPairs) {
      return res.status(503).json({
        error: 'GRU transport config not available (run from monorepo with config/gru-transport-active.json)',
      });
    }

    return res.json({
      ...loader.getGruTransportMetadata(),
      transportPairs: loader.getActiveTransportPairs(),
      publicPools: loader.getActivePublicPools ? loader.getActivePublicPools() : [],
    });
  }
);

/**
 * GET /api/v1/token-mapping/resolve?fromChain=138&toChain=56&address=0x...
 * Returns mapped token address on target chain.
 */
router.get(
  '/resolve',
  cacheMiddleware(5 * 60 * 1000),
  (req: Request, res: Response) => {
    const fromChain = parseInt(String(req.query.fromChain ?? req.query.from), 10);
    const toChain = parseInt(String(req.query.toChain ?? req.query.to), 10);
    const address = String(req.query.address ?? req.query.token ?? '').trim();
    if (!Number.isFinite(fromChain) || !Number.isFinite(toChain) || !address) {
      return res.status(400).json({
        error: 'Missing or invalid fromChain, toChain, or address',
        example: '/api/v1/token-mapping/resolve?fromChain=138&toChain=56&address=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
      });
    }

    const loader = loadMultichainLoader();
    if (!loader) {
      return res.status(503).json({
        error: 'Token mapping config not available (run from monorepo)',
      });
    }

    const mapped = loader.getMappedAddress(fromChain, toChain, address);
    const activeTransportPair = loader.getActiveTransportPair
      ? loader.getActiveTransportPair(fromChain, toChain, {
          address,
          targetTokenAddress: mapped ?? null,
        })
      : null;
    const canonicalTokenIdentifier =
      (activeTransportPair && typeof activeTransportPair.canonicalSymbol === 'string'
        ? activeTransportPair.canonicalSymbol
        : null) ??
      address;
    const canonicalToken = loader.getEnabledCanonicalToken
      ? loader.getEnabledCanonicalToken(canonicalTokenIdentifier)
      : null;
    return res.json({
      fromChainId: fromChain,
      toChainId: toChain,
      addressOnSource: address,
      addressOnTarget: mapped ?? null,
      activeTransportEligible: !!activeTransportPair && activeTransportPair.eligible === true,
      gruTransportRuntimeReady: !!activeTransportPair && activeTransportPair.runtimeReady === true,
      gruTransportPairKey:
        activeTransportPair && typeof activeTransportPair.key === 'string' ? activeTransportPair.key : null,
      gruTransportAssetClass:
        activeTransportPair && typeof activeTransportPair.assetClass === 'string'
          ? activeTransportPair.assetClass
          : null,
      gruTransportFamilyKey:
        activeTransportPair && typeof activeTransportPair.familyKey === 'string'
          ? activeTransportPair.familyKey
          : null,
      gruTransportBackingMode:
        activeTransportPair && typeof activeTransportPair.backingMode === 'string'
          ? activeTransportPair.backingMode
          : null,
      gruTransportRedeemPolicy:
        activeTransportPair && typeof activeTransportPair.redeemPolicy === 'string'
          ? activeTransportPair.redeemPolicy
          : null,
      gruTransportWrappedNativeQuoteSymbol:
        activeTransportPair && typeof activeTransportPair.wrappedNativeQuoteSymbol === 'string'
          ? activeTransportPair.wrappedNativeQuoteSymbol
          : null,
      gruTransportStableQuoteSymbol:
        activeTransportPair && typeof activeTransportPair.stableQuoteSymbol === 'string'
          ? activeTransportPair.stableQuoteSymbol
          : null,
      gruTransportReferenceVenue:
        activeTransportPair && typeof activeTransportPair.referenceVenue === 'string'
          ? activeTransportPair.referenceVenue
          : null,
      gruGasRedemptionPathAllowed:
        activeTransportPair &&
        typeof activeTransportPair.familyKey === 'string' &&
        loader.isGasRedemptionPathAllowed
          ? loader.isGasRedemptionPathAllowed(fromChain, toChain, activeTransportPair.familyKey)
          : null,
      gruTransportCanonicalToken: canonicalToken,
    });
  }
);

export default router;
