import { AbiCoder } from 'ethers';
import {
  PlannerProvider,
  ProviderCapabilityRecord,
  ProviderPairCapability,
} from '../services/planner-v2-types';
import { encodeChain138DodoV3ProviderData, isChain138DodoV3ExecutionLive } from '../services/dodo-v3-pilot';
import { getChain138PilotVenueEdges } from '../services/chain138-pilot-venues';
import { getChain138RoutingAssets } from './routing-assets';

const abiCoder = AbiCoder.defaultAbiCoder();
const CHAIN_138 = 138;
const CHAIN138_UNISWAP_V3_ROUTER = '0xde9cd8ee2811e6e64a41d5f68be315d33995975e';
const CHAIN138_UNISWAP_V3_QUOTER = '0x6abbb1ceb2468e748a03a00cd6aa9bfe893afa1f';
const CHAIN138_PILOT_BALANCER_VAULT = '0x96423d7c1727698d8a25ebfb88131e9422d1a3c3';
const CHAIN138_PILOT_CURVE_3POOL = '0xe440ec15805be4c7babcd17a63b8c8a08a492e0f';
const CHAIN138_PILOT_ONEINCH_ROUTER = '0x500b84b1bc6f59c1898a5fe538ea20a758757a4f';
const CHAIN138_PILOT_BALANCER_WETH_USDT_POOL_ID = '0x877cd220759e8c94b82f55450c85d382ae06856c426b56d93092a420facbc324';
const CHAIN138_PILOT_BALANCER_WETH_USDC_POOL_ID = '0xd8dfb18a6baf9b29d8c2dbd74639db87ac558af120df5261dab8e2a5de69013b';

function normalizeAddress(value?: string): string {
  return String(value || '').trim().toLowerCase();
}

function liveOrPlannedAddress(value?: string): 'live' | 'planned' {
  return normalizeAddress(value) ? 'live' : 'planned';
}

function bidirectionalPair(args: {
  chainId: number;
  provider: PlannerProvider;
  tokenASymbol: string;
  tokenAAddress: string;
  tokenBSymbol: string;
  tokenBAddress: string;
  status: 'live' | 'planned' | 'blocked';
  target?: string;
  providerData?: Record<string, unknown>;
  providerDataHex?: string;
  notes?: string[];
  reason?: string;
}): ProviderPairCapability[] {
  return [
    {
      chainId: args.chainId,
      provider: args.provider,
      legType: 'swap',
      status: args.status,
      tokenInSymbol: args.tokenASymbol,
      tokenInAddress: normalizeAddress(args.tokenAAddress),
      tokenOutSymbol: args.tokenBSymbol,
      tokenOutAddress: normalizeAddress(args.tokenBAddress),
      target: normalizeAddress(args.target),
      providerData: args.providerData,
      providerDataHex: args.providerDataHex,
      notes: args.notes,
      reason: args.reason,
    },
    {
      chainId: args.chainId,
      provider: args.provider,
      legType: 'swap',
      status: args.status,
      tokenInSymbol: args.tokenBSymbol,
      tokenInAddress: normalizeAddress(args.tokenBAddress),
      tokenOutSymbol: args.tokenASymbol,
      tokenOutAddress: normalizeAddress(args.tokenAAddress),
      target: normalizeAddress(args.target),
      providerData: args.providerData,
      providerDataHex: args.providerDataHex,
      notes: args.notes,
      reason: args.reason,
    },
  ];
}

function encodeDodoPool(poolAddress: string): string {
  return abiCoder.encode(['address'], [poolAddress]);
}

function encodeUniswapRoute(fee: number, quoter: string): string {
  return abiCoder.encode(['bytes', 'uint24', 'address', 'bool'], ['0x', fee, quoter, false]);
}

function encodeBalancerRoute(poolId: string): string {
  return abiCoder.encode(['bytes32'], [poolId]);
}

function encodeCurveRoute(i: number, j: number, useUnderlying: boolean): string {
  return abiCoder.encode(['int128', 'int128', 'bool'], [i, j, useUnderlying]);
}

function encodeOneInchRoute(router: string): string {
  return abiCoder.encode(['address', 'address', 'bytes'], [router, router, '0x']);
}

function encodeRouterV2Route(factory: string, router: string): string {
  return abiCoder.encode(['address', 'address'], [factory, router]);
}

function chain138DodoCapabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const dodoProvider =
    normalizeAddress(process.env.DODO_PMM_PROVIDER_ADDRESS) ||
    normalizeAddress(process.env.DODO_PMM_PROVIDER) ||
    '0x3f729632e9553ebaccde2e9b4c8f2b285b014f2e';

  const stablePool = '0x9e89bae009adf128782e19e8341996c596ac40dc';
  const cusdtUsdtPool = '0x866cb44b59303d8dc5f4f9e3e7a8e8b0bf238d66';
  const cusdcUsdcPool = '0xc39b7d0f40838cbfb54649d327f49a6dac964062';
  const cusdtXaucPool = '0x1aa55e2001e5651349aff5a63fd7a7ae44f0f1b0';
  const cusdcXaucPool = '0xea9ac6357cacb42a83b9082b870610363b177cba';
  const ceurtXaucPool = '0xba99bc1eaac164569d5aca96c806934ddaf970cf';
  const cbtcCusdtPool = normalizeAddress(process.env.CHAIN138_POOL_CBTC_CUSDT);
  const cbtcCusdcPool = normalizeAddress(process.env.CHAIN138_POOL_CBTC_CUSDC);
  const cbtcXaucPool = normalizeAddress(process.env.CHAIN138_POOL_CBTC_CXAUC);
  const wethUsdtPool = normalizeAddress(process.env.CHAIN138_POOL_WETH_USDT);
  const wethUsdcPool = normalizeAddress(process.env.CHAIN138_POOL_WETH_USDC);

  const pairs: ProviderPairCapability[] = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cUSDT',
      tokenAAddress: assets.cUSDT.address,
      tokenBSymbol: 'cUSDC',
      tokenBAddress: assets.cUSDC.address,
      status: 'live',
      target: dodoProvider,
      providerData: { poolAddress: stablePool },
      providerDataHex: encodeDodoPool(stablePool),
      notes: ['Canonical stable pool on the official DODO V2 DVM-backed stack.'],
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cUSDT',
      tokenAAddress: assets.cUSDT.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status: 'live',
      target: dodoProvider,
      providerData: { poolAddress: cusdtUsdtPool },
      providerDataHex: encodeDodoPool(cusdtUsdtPool),
      notes: ['Canonical stable pool on the official DODO V2 DVM-backed stack.'],
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cUSDC',
      tokenAAddress: assets.cUSDC.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status: 'live',
      target: dodoProvider,
      providerData: { poolAddress: cusdcUsdcPool },
      providerDataHex: encodeDodoPool(cusdcUsdcPool),
      notes: ['Canonical stable pool on the official DODO V2 DVM-backed stack.'],
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cUSDT',
      tokenAAddress: assets.cUSDT.address,
      tokenBSymbol: 'cXAUC',
      tokenBAddress: assets.cXAUC.address,
      status: 'live',
      target: dodoProvider,
      providerData: { poolAddress: cusdtXaucPool },
      providerDataHex: encodeDodoPool(cusdtXaucPool),
      notes: ['Commodity route; excluded unless policy allows commodity intermediates.'],
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cUSDC',
      tokenAAddress: assets.cUSDC.address,
      tokenBSymbol: 'cXAUC',
      tokenBAddress: assets.cXAUC.address,
      status: 'live',
      target: dodoProvider,
      providerData: { poolAddress: cusdcXaucPool },
      providerDataHex: encodeDodoPool(cusdcXaucPool),
      notes: ['Commodity route; excluded unless policy allows commodity intermediates.'],
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cEURT',
      tokenAAddress: assets.cEURT.address,
      tokenBSymbol: 'cXAUC',
      tokenBAddress: assets.cXAUC.address,
      status: 'live',
      target: dodoProvider,
      providerData: { poolAddress: ceurtXaucPool },
      providerDataHex: encodeDodoPool(ceurtXaucPool),
      notes: ['Commodity route; excluded unless policy allows commodity intermediates.'],
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cBTC',
      tokenAAddress: assets.cBTC.address,
      tokenBSymbol: 'cUSDT',
      tokenBAddress: assets.cUSDT.address,
      status: liveOrPlannedAddress(cbtcCusdtPool),
      target: dodoProvider,
      providerData: cbtcCusdtPool ? { poolAddress: cbtcCusdtPool } : undefined,
      providerDataHex: cbtcCusdtPool ? encodeDodoPool(cbtcCusdtPool) : undefined,
      notes: ['Bitcoin monetary-unit route for the jewelry-box program.'],
      reason: cbtcCusdtPool ? undefined : 'Set CHAIN138_POOL_CBTC_CUSDT after the canonical cBTC/cUSDT PMM pool is created and funded.',
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cBTC',
      tokenAAddress: assets.cBTC.address,
      tokenBSymbol: 'cUSDC',
      tokenBAddress: assets.cUSDC.address,
      status: liveOrPlannedAddress(cbtcCusdcPool),
      target: dodoProvider,
      providerData: cbtcCusdcPool ? { poolAddress: cbtcCusdcPool } : undefined,
      providerDataHex: cbtcCusdcPool ? encodeDodoPool(cbtcCusdcPool) : undefined,
      notes: ['Bitcoin monetary-unit route for the jewelry-box program.'],
      reason: cbtcCusdcPool ? undefined : 'Set CHAIN138_POOL_CBTC_CUSDC after the canonical cBTC/cUSDC PMM pool is created and funded.',
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'cBTC',
      tokenAAddress: assets.cBTC.address,
      tokenBSymbol: 'cXAUC',
      tokenBAddress: assets.cXAUC.address,
      status: liveOrPlannedAddress(cbtcXaucPool),
      target: dodoProvider,
      providerData: cbtcXaucPool ? { poolAddress: cbtcXaucPool } : undefined,
      providerDataHex: cbtcXaucPool ? encodeDodoPool(cbtcXaucPool) : undefined,
      notes: ['Bitcoin-to-gold route for jewelry-box rebalances; excluded unless policy allows commodity intermediates.'],
      reason: cbtcXaucPool ? undefined : 'Set CHAIN138_POOL_CBTC_CXAUC after the canonical cBTC/cXAUC PMM pool is created and funded.',
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status: wethUsdtPool ? 'live' : 'planned',
      target: dodoProvider,
      providerData: wethUsdtPool ? { poolAddress: wethUsdtPool } : undefined,
      providerDataHex: wethUsdtPool ? encodeDodoPool(wethUsdtPool) : undefined,
      notes: ['Phase 1 WETH lane for router-v2 stable execution.'],
      reason: wethUsdtPool ? undefined : 'Set CHAIN138_POOL_WETH_USDT after the canonical WETH/USDT pool is created and funded.',
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status: wethUsdcPool ? 'live' : 'planned',
      target: dodoProvider,
      providerData: wethUsdcPool ? { poolAddress: wethUsdcPool } : undefined,
      providerDataHex: wethUsdcPool ? encodeDodoPool(wethUsdcPool) : undefined,
      notes: ['Phase 1 WETH lane for router-v2 stable execution.'],
      reason: wethUsdcPool ? undefined : 'Set CHAIN138_POOL_WETH_USDC after the canonical WETH/USDC pool is created and funded.',
    }),
  ];

  const livePairs = pairs.filter((pair) => pair.status === 'live');
  return {
    chainId: CHAIN_138,
    provider: 'dodo',
    executionMode: 'onchain',
    live: livePairs.length > 0,
    quoteLive: livePairs.length > 0,
    executionLive: livePairs.length > 0,
    supportedLegTypes: ['swap'],
    pairs,
    notes: ['DODO is the first production executor for Chain 138 router-v2 rollout.'],
  };
}

function chain138DodoV3Capabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const enabled = process.env.CHAIN138_ENABLE_DODO_V3_ROUTING !== '0';
  const proxy = normalizeAddress(process.env.CHAIN138_D3_PROXY_ADDRESS) || '0xc9a11abb7c63d88546be24d58a6d95e3762cb843';
  const pool = normalizeAddress(process.env.CHAIN138_D3_MM_ADDRESS) || '0x6550a3a59070061a262a893a1d6f3f490affdbda';
  const status = enabled && proxy && pool ? 'live' : 'planned';
  const executionLive = status === 'live' && isChain138DodoV3ExecutionLive();

  const pairs = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'dodo_v3',
      tokenASymbol: 'WETH10',
      tokenAAddress: assets.WETH10.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status,
      target: proxy,
      providerData: status === 'live'
        ? { poolAddress: pool, proxyAddress: proxy, quoteMethod: 'querySellTokens' }
        : undefined,
      providerDataHex: executionLive ? encodeChain138DodoV3ProviderData(pool) : undefined,
      notes: [
        'Canonical Chain 138 DODO v3 / D3MM pilot route.',
        executionLive
          ? 'Planner visibility, quote selection, and EnhancedSwapRouterV2 execution are live for the canonical pilot pair.'
          : 'Planner visibility and quote selection are live; EnhancedSwapRouterV2 adapter support is still pending.',
      ],
      reason: status === 'planned'
        ? 'Set CHAIN138_ENABLE_DODO_V3_ROUTING=1 and ensure CHAIN138_D3_MM_ADDRESS / CHAIN138_D3_PROXY_ADDRESS are configured to expose the pilot venue.'
        : undefined,
    }),
  ];

  return {
    chainId: CHAIN_138,
    provider: 'dodo_v3',
    executionMode: 'onchain',
    live: status === 'live',
    quoteLive: status === 'live',
    executionLive,
    supportedLegTypes: ['swap'],
    pairs,
    notes: [
      executionLive
        ? 'Private DODO v3 / D3MM Chain 138 pilot is live in planner-v2 visibility and internal execution-plan calldata.'
        : 'Private DODO v3 / D3MM Chain 138 pilot promoted into planner-v2 visibility.',
      executionLive
        ? 'Route discovery and execution-plan generation are live for the canonical pilot pair.'
        : 'Route discovery is live, but internal execution-plan calldata is intentionally withheld until a dedicated D3 route executor adapter exists.',
    ],
  };
}

function chain138UniswapCapabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const router = normalizeAddress(process.env.UNISWAP_V3_ROUTER || CHAIN138_UNISWAP_V3_ROUTER);
  const quoter = normalizeAddress(process.env.UNISWAP_QUOTER_ADDRESS || process.env.UNISWAP_QUOTER || CHAIN138_UNISWAP_V3_QUOTER);
  const wethUsdtFee = Number(process.env.UNISWAP_V3_WETH_USDT_FEE || '500');
  const wethUsdcFee = Number(process.env.UNISWAP_V3_WETH_USDC_FEE || '500');
  const status = router && quoter ? 'live' : 'planned';

  const pairs = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'uniswap_v3',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status,
      target: router,
      providerData: status === 'live' ? { fee: wethUsdtFee, quoter } : undefined,
      providerDataHex: status === 'live' ? encodeUniswapRoute(wethUsdtFee, quoter) : undefined,
      notes: ['Canonical Chain 138 upstream-native Uniswap v3 WETH/USDT venue.'],
      reason: status === 'planned' ? 'Configure UNISWAP_V3_ROUTER and UNISWAP_QUOTER_ADDRESS after Chain 138 native venue deployment.' : undefined,
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'uniswap_v3',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status,
      target: router,
      providerData: status === 'live' ? { fee: wethUsdcFee, quoter } : undefined,
      providerDataHex: status === 'live' ? encodeUniswapRoute(wethUsdcFee, quoter) : undefined,
      notes: ['Canonical Chain 138 upstream-native Uniswap v3 WETH/USDC venue.'],
      reason: status === 'planned' ? 'Configure UNISWAP_V3_ROUTER and UNISWAP_QUOTER_ADDRESS after Chain 138 native venue deployment.' : undefined,
    }),
  ];

  return {
    chainId: CHAIN_138,
    provider: 'uniswap_v3',
    executionMode: 'onchain',
    live: status === 'live',
    quoteLive: status === 'live',
    executionLive: status === 'live',
    supportedLegTypes: ['swap'],
    pairs,
    notes: ['Canonical Chain 138 upstream-native Uniswap v3 router/quoter path.'],
  };
}

function chain138UniswapV2Capabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const factory = normalizeAddress(process.env.CHAIN_138_UNISWAP_V2_FACTORY);
  const router = normalizeAddress(process.env.CHAIN_138_UNISWAP_V2_ROUTER);
  const wethUsdtPair = normalizeAddress(process.env.CHAIN138_UNISWAP_V2_NATIVE_WETH_USDT_PAIR);
  const wethUsdcPair = normalizeAddress(process.env.CHAIN138_UNISWAP_V2_NATIVE_WETH_USDC_PAIR);
  const cusdtCusdcPair = normalizeAddress(process.env.CHAIN138_UNISWAP_V2_NATIVE_CUSDT_CUSDC_PAIR);
  const status = factory && router ? 'live' : 'planned';

  const pairs = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'uniswap_v2',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status,
      target: router,
      providerData: status === 'live' ? { factory, router, pair: wethUsdtPair } : undefined,
      providerDataHex: status === 'live' ? encodeRouterV2Route(factory, router) : undefined,
      notes: ['Canonical Chain 138 native Uniswap v2 WETH/USDT venue.'],
      reason: status === 'planned' ? 'Configure CHAIN_138_UNISWAP_V2_FACTORY and CHAIN_138_UNISWAP_V2_ROUTER after Chain 138 native venue deployment.' : undefined,
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'uniswap_v2',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status,
      target: router,
      providerData: status === 'live' ? { factory, router, pair: wethUsdcPair } : undefined,
      providerDataHex: status === 'live' ? encodeRouterV2Route(factory, router) : undefined,
      notes: ['Canonical Chain 138 native Uniswap v2 WETH/USDC venue.'],
      reason: status === 'planned' ? 'Configure CHAIN_138_UNISWAP_V2_FACTORY and CHAIN_138_UNISWAP_V2_ROUTER after Chain 138 native venue deployment.' : undefined,
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'uniswap_v2',
      tokenASymbol: 'cUSDT',
      tokenAAddress: assets.cUSDT.address,
      tokenBSymbol: 'cUSDC',
      tokenBAddress: assets.cUSDC.address,
      status,
      target: router,
      providerData: status === 'live' ? { factory, router, pair: cusdtCusdcPair } : undefined,
      providerDataHex: status === 'live' ? encodeRouterV2Route(factory, router) : undefined,
      notes: ['Canonical Chain 138 native Uniswap v2 GRU stable venue.'],
      reason: status === 'planned' ? 'Configure CHAIN_138_UNISWAP_V2_FACTORY and CHAIN_138_UNISWAP_V2_ROUTER after Chain 138 native venue deployment.' : undefined,
    }),
  ];

  return {
    chainId: CHAIN_138,
    provider: 'uniswap_v2',
    executionMode: 'onchain',
    live: status === 'live',
    quoteLive: status === 'live',
    executionLive: status === 'live',
    supportedLegTypes: ['swap'],
    pairs,
    notes: ['Canonical Chain 138 native Uniswap v2 router/factory path.'],
  };
}

function chain138SushiswapCapabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const factory = normalizeAddress(process.env.CHAIN_138_SUSHISWAP_FACTORY);
  const router = normalizeAddress(process.env.CHAIN_138_SUSHISWAP_ROUTER);
  const wethUsdtPair = normalizeAddress(process.env.CHAIN138_SUSHISWAP_NATIVE_WETH_USDT_PAIR);
  const wethUsdcPair = normalizeAddress(process.env.CHAIN138_SUSHISWAP_NATIVE_WETH_USDC_PAIR);
  const cusdtCusdcPair = normalizeAddress(process.env.CHAIN138_SUSHISWAP_NATIVE_CUSDT_CUSDC_PAIR);
  const status = factory && router ? 'live' : 'planned';

  const pairs = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'sushiswap',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status,
      target: router,
      providerData: status === 'live' ? { factory, router, pair: wethUsdtPair } : undefined,
      providerDataHex: status === 'live' ? encodeRouterV2Route(factory, router) : undefined,
      notes: ['Canonical Chain 138 native SushiSwap-compatible WETH/USDT venue.'],
      reason: status === 'planned' ? 'Configure CHAIN_138_SUSHISWAP_FACTORY and CHAIN_138_SUSHISWAP_ROUTER after Chain 138 Sushi deployment.' : undefined,
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'sushiswap',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status,
      target: router,
      providerData: status === 'live' ? { factory, router, pair: wethUsdcPair } : undefined,
      providerDataHex: status === 'live' ? encodeRouterV2Route(factory, router) : undefined,
      notes: ['Canonical Chain 138 native SushiSwap-compatible WETH/USDC venue.'],
      reason: status === 'planned' ? 'Configure CHAIN_138_SUSHISWAP_FACTORY and CHAIN_138_SUSHISWAP_ROUTER after Chain 138 Sushi deployment.' : undefined,
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'sushiswap',
      tokenASymbol: 'cUSDT',
      tokenAAddress: assets.cUSDT.address,
      tokenBSymbol: 'cUSDC',
      tokenBAddress: assets.cUSDC.address,
      status,
      target: router,
      providerData: status === 'live' ? { factory, router, pair: cusdtCusdcPair } : undefined,
      providerDataHex: status === 'live' ? encodeRouterV2Route(factory, router) : undefined,
      notes: ['Canonical Chain 138 native SushiSwap-compatible GRU stable venue.'],
      reason: status === 'planned' ? 'Configure CHAIN_138_SUSHISWAP_FACTORY and CHAIN_138_SUSHISWAP_ROUTER after Chain 138 Sushi deployment.' : undefined,
    }),
  ];

  return {
    chainId: CHAIN_138,
    provider: 'sushiswap',
    executionMode: 'onchain',
    live: status === 'live',
    quoteLive: status === 'live',
    executionLive: status === 'live',
    supportedLegTypes: ['swap'],
    pairs,
    notes: ['Canonical Chain 138 native SushiSwap-compatible router/factory path.'],
  };
}

function chain138BalancerCapabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const vault = normalizeAddress(process.env.BALANCER_VAULT || CHAIN138_PILOT_BALANCER_VAULT);
  const wethUsdtPoolId = process.env.BALANCER_WETH_USDT_POOL_ID || CHAIN138_PILOT_BALANCER_WETH_USDT_POOL_ID;
  const wethUsdcPoolId = process.env.BALANCER_WETH_USDC_POOL_ID || CHAIN138_PILOT_BALANCER_WETH_USDC_POOL_ID;

  const pairs = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'balancer',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status: vault && wethUsdtPoolId ? 'live' : 'planned',
      target: vault,
      providerData: vault && wethUsdtPoolId ? { poolId: wethUsdtPoolId } : undefined,
      providerDataHex: vault && wethUsdtPoolId ? encodeBalancerRoute(wethUsdtPoolId) : undefined,
      notes: ['Enabled only after Chain 138 Balancer pool IDs are set.'],
      reason: vault && wethUsdtPoolId ? undefined : 'Configure BALANCER_VAULT and BALANCER_WETH_USDT_POOL_ID once the pool exists.',
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'balancer',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status: vault && wethUsdcPoolId ? 'live' : 'planned',
      target: vault,
      providerData: vault && wethUsdcPoolId ? { poolId: wethUsdcPoolId } : undefined,
      providerDataHex: vault && wethUsdcPoolId ? encodeBalancerRoute(wethUsdcPoolId) : undefined,
      notes: ['Enabled only after Chain 138 Balancer pool IDs are set.'],
      reason: vault && wethUsdcPoolId ? undefined : 'Configure BALANCER_VAULT and BALANCER_WETH_USDC_POOL_ID once the pool exists.',
    }),
  ];

  return {
    chainId: CHAIN_138,
    provider: 'balancer',
    executionMode: 'onchain',
    live: pairs.some((pair) => pair.status === 'live'),
    quoteLive: pairs.some((pair) => pair.status === 'live'),
    executionLive: pairs.some((pair) => pair.status === 'live'),
    supportedLegTypes: ['swap'],
    pairs,
    notes: ['Balancer stays disabled until the minimum viable Chain 138 venue set exists.'],
  };
}

function chain138CurveCapabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const curvePool = normalizeAddress(process.env.CURVE_3POOL || CHAIN138_PILOT_CURVE_3POOL);
  const status = liveOrPlannedAddress(curvePool);

  const pairs = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'curve',
      tokenASymbol: 'USDT',
      tokenAAddress: assets.USDT.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status,
      target: curvePool,
      providerData: status === 'live' ? { i: 0, j: 1, useUnderlying: false } : undefined,
      providerDataHex: status === 'live' ? encodeCurveRoute(0, 1, false) : undefined,
      notes: ['Curve is reserved for stable-stable legs; no direct WETH path is configured.'],
      reason: status === 'planned' ? 'Configure CURVE_3POOL once the Chain 138 stable-stable venue is live.' : undefined,
    }),
  ];

  return {
    chainId: CHAIN_138,
    provider: 'curve',
    executionMode: 'onchain',
    live: status === 'live',
    quoteLive: status === 'live',
    executionLive: status === 'live',
    supportedLegTypes: ['swap'],
    pairs,
    notes: ['Curve is intentionally constrained to stable-stable execution for router-v2.'],
  };
}

function chain138PartnerCapabilities(): ProviderCapabilityRecord {
  return {
    chainId: CHAIN_138,
    provider: 'partner',
    executionMode: 'partner',
    live: false,
    quoteLive: false,
    executionLive: false,
    supportedLegTypes: ['swap', 'bridge'],
    pairs: [],
    notes: ['1inch, 0x, and LiFi remain partner payload adapters until explicit Chain 138 live support is verified.'],
  };
}

function chain138OneInchCapabilities(): ProviderCapabilityRecord {
  const assets = getChain138RoutingAssets();
  const router = normalizeAddress(process.env.ONEINCH_ROUTER || CHAIN138_PILOT_ONEINCH_ROUTER);
  const status = router ? 'live' : 'planned';

  const pairs = [
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'one_inch',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDT',
      tokenBAddress: assets.USDT.address,
      status,
      target: router,
      providerData: status === 'live' ? { executor: router, allowanceTarget: router } : undefined,
      providerDataHex: status === 'live' ? encodeOneInchRoute(router) : undefined,
      notes: ['Enabled after the Chain 138 pilot-compatible 1inch router is deployed and funded.'],
      reason: status === 'planned' ? 'Configure ONEINCH_ROUTER once the Chain 138 pilot-compatible router is live.' : undefined,
    }),
    ...bidirectionalPair({
      chainId: CHAIN_138,
      provider: 'one_inch',
      tokenASymbol: 'WETH',
      tokenAAddress: assets.WETH.address,
      tokenBSymbol: 'USDC',
      tokenBAddress: assets.USDC.address,
      status,
      target: router,
      providerData: status === 'live' ? { executor: router, allowanceTarget: router } : undefined,
      providerDataHex: status === 'live' ? encodeOneInchRoute(router) : undefined,
      notes: ['Enabled after the Chain 138 pilot-compatible 1inch router is deployed and funded.'],
      reason: status === 'planned' ? 'Configure ONEINCH_ROUTER once the Chain 138 pilot-compatible router is live.' : undefined,
    }),
  ];

  return {
    chainId: CHAIN_138,
    provider: 'one_inch',
    executionMode: 'onchain',
    live: status === 'live',
    quoteLive: status === 'live',
    executionLive: status === 'live',
    supportedLegTypes: ['swap'],
    pairs,
    notes: ['1inch is promoted from partner-only placeholder to an executable Chain 138 pilot-compatible router when configured.'],
  };
}

export function getProviderCapabilities(chainId: number): ProviderCapabilityRecord[] {
  if (chainId !== CHAIN_138) return [];
  return [
    chain138DodoCapabilities(),
    chain138DodoV3Capabilities(),
    chain138UniswapCapabilities(),
    chain138UniswapV2Capabilities(),
    chain138SushiswapCapabilities(),
    chain138BalancerCapabilities(),
    chain138CurveCapabilities(),
    chain138OneInchCapabilities(),
    chain138PartnerCapabilities(),
  ];
}

export function findProviderPairCapability(
  chainId: number,
  provider: PlannerProvider,
  tokenInAddress: string,
  tokenOutAddress: string
): ProviderPairCapability | undefined {
  const normalizedIn = normalizeAddress(tokenInAddress);
  const normalizedOut = normalizeAddress(tokenOutAddress);
  return getProviderCapabilities(chainId)
    .find((record) => record.provider === provider)
    ?.pairs.find(
      (pair) =>
        pair.tokenInAddress === normalizedIn &&
        pair.tokenOutAddress === normalizedOut
    );
}
