import { isChain138CanonicalLivePool } from './chain138-live-dodo-pools';
import { loadTokenMappingLoader } from './repo-config-loader';

export interface ConfigRef {
  address?: string | null;
  env?: string | null;
}

export interface GruTransportSystemMetadata {
  name: string;
  shortName: string;
  canonicalChainId: number;
  canonicalChainName?: string;
  transportClass?: string;
  publicPoolModel?: string;
  hardPegTruth?: string;
  wethTransportSeparated?: boolean;
  notes?: string;
}

export interface GruTransportMetadata {
  system: GruTransportSystemMetadata | null;
  terminology: Record<string, string>;
  enabledCanonicalTokens: Array<Record<string, unknown>>;
  enabledDestinationChains: Array<Record<string, unknown>>;
  gasAssetFamilies?: GruTransportGasAssetFamily[];
  gasRedeemGroups?: GruTransportGasRedeemGroup[];
  gasProtocolExposure?: GruTransportGasProtocolExposure[];
  counts: {
    enabledCanonicalTokens: number;
    enabledDestinationChains: number;
    approvedBridgePeers: number;
    transportPairs: number;
    configuredTransportPairs?: number;
    deferredTransportPairs?: number;
    gasAssetFamilies?: number;
    gasRedeemGroups?: number;
    gasProtocolExposure?: number;
    gasTransportPairs?: number;
    strictEscrowTransportPairs?: number;
    hybridCapTransportPairs?: number;
    eligibleTransportPairs?: number;
    runtimeReadyTransportPairs?: number;
    publicPools: number;
    activePublicPools?: number;
    routablePublicPools?: number;
    mcpVisiblePublicPools?: number;
  };
}

export interface GruTransportGasAssetFamily {
  familyKey: string;
  active?: boolean;
  status?: string;
  canonicalSymbol138: string;
  mirroredSymbol: string;
  assetClass: string;
  originChains: number[];
  laneGroup: string;
  backingMode: string;
  redeemPolicy: string;
  wrappedNativeQuoteSymbol: string;
  stableQuoteSymbol: string;
  referenceVenue: string;
  perLaneCaps?: Record<string, string>;
  displayAliases?: Record<string, string>;
  hubRebalance?: Record<string, unknown>;
}

export interface GruTransportGasRedeemGroup {
  key: string;
  familyKey: string;
  allowedChains: number[];
  redeemPolicy: string;
  description?: string;
}

export interface GruTransportGasProtocolExposure {
  key: string;
  chainId: number;
  active?: boolean;
  status?: string;
  familyKey: string;
  mirroredSymbol: string;
  backingMode: string;
  dodoPmm?: Record<string, unknown>;
  uniswapV3?: Record<string, unknown>;
  balancer?: Record<string, unknown>;
  curve?: Record<string, unknown>;
  oneInch?: Record<string, unknown>;
}

export interface GruTransportBridgePeer {
  key: string;
  chainId: number;
  chainName: string;
  ccipChainSelector?: string;
  active?: boolean;
  status?: string;
  bridgeKind: string;
  l1Bridge?: ConfigRef;
  l2Bridge?: ConfigRef;
  freezeTokenPairRequired?: boolean;
  freezeDestinationRequired?: boolean;
}

export interface GruTransportPair {
  key: string;
  canonicalChainId: number;
  destinationChainId: number;
  destinationChainName?: string | null;
  destinationChainSelector?: string | null;
  active?: boolean;
  status?: string;
  canonicalSymbol: string;
  mirroredSymbol: string;
  /** From gru-transport-active enabledCanonicalTokens[].bridge.canonicalAssetVersion */
  bridgeCanonicalAssetVersion?: string;
  /** From gru-transport-active enabledCanonicalTokens[].bridge.mirroredAssetVersion */
  bridgeMirroredAssetVersion?: string;
  mappingKey: string;
  peerKey: string;
  phase?: string;
  routeDiscoveryEnabled?: boolean;
  mcpVisible?: boolean;
  reserveVerifierKey?: string;
  maxOutstanding?: {
    required?: boolean;
    amount?: string;
    env?: string;
  };
  publicPoolKeys?: string[];
  assetClass?: string;
  familyKey?: string;
  laneGroup?: string;
  backingMode?: string;
  redeemPolicy?: string;
  wrappedNativeQuoteSymbol?: string;
  stableQuoteSymbol?: string;
  referenceVenue?: string;
  protocolExposureKey?: string;
  supplyAccounting?: Record<string, unknown>;
  canonicalAddress?: string | null;
  mirroredAddress?: string | null;
  mirrorDeploymentAddress?: string | null;
  peer?: GruTransportBridgePeer | null;
  mappingFound?: boolean;
  mirrorDeployed?: boolean;
  canonicalEnabled?: boolean;
  destinationEnabled?: boolean;
  bridgeAvailable?: boolean | null;
  bridgePeerConfigured?: boolean;
  maxOutstandingConfigured?: boolean;
  reserveVerifierConfigured?: boolean;
  runtimeL1BridgeAddress?: string | null;
  runtimeL2BridgeAddress?: string | null;
  runtimeBridgeReady?: boolean;
  runtimeMaxOutstandingValue?: string | null;
  runtimeMaxOutstandingReady?: boolean;
  runtimeReserveVerifierBridgeAddress?: string | null;
  runtimeReserveVerifierAddress?: string | null;
  runtimeReserveVaultAddress?: string | null;
  runtimeReserveSystemAddress?: string | null;
  runtimeReserveVerifierReady?: boolean;
  runtimeOutstandingValue?: string | null;
  runtimeEscrowedValue?: string | null;
  runtimeTreasuryBackedValue?: string | null;
  runtimeTreasuryCapValue?: string | null;
  runtimeSupplyAccountingReady?: boolean | null;
  supplyInvariantSatisfied?: boolean | null;
  protocolExposure?: GruTransportGasProtocolExposure | null;
  runtimeMissingRequirements?: string[];
  eligibilityBlockers?: string[];
  runtimeReady?: boolean;
  eligible?: boolean;
}

export interface GruTransportPublicPool {
  key: string;
  chainId: number;
  baseSymbol: string;
  quoteSymbol: string;
  poolAddress?: string | null;
  active?: boolean;
  routingEnabled?: boolean;
  mcpVisible?: boolean;
  phase?: string;
}

export interface PublicPoolLike {
  poolAddress: string;
  token0Address: string;
  token1Address: string;
}

interface GruTransportLoader {
  getGruTransportMetadata?: () => GruTransportMetadata | null;
  getGasAssetFamilies?: () => GruTransportGasAssetFamily[];
  getGasRedeemGroups?: () => GruTransportGasRedeemGroup[];
  getGasProtocolExposure?: () => GruTransportGasProtocolExposure[];
  isGasRedemptionPathAllowed?: (fromChainId: number, toChainId: number, identifier: string) => boolean;
  getActiveTransportPairs?: () => GruTransportPair[];
  getActiveTransportPair?: (
    fromChainId: number,
    toChainId: number,
    criteria?: Record<string, unknown>
  ) => GruTransportPair | null;
  getApprovedBridgePeer?: (chainId: number) => GruTransportBridgePeer | null;
  getActivePublicPools?: () => GruTransportPublicPool[];
  isPublicPoolActive?: (chainId: number, poolAddress: string) => boolean;
  isPublicPoolRoutable?: (chainId: number, poolAddress: string) => boolean;
  isPublicPoolMcpVisible?: (chainId: number, poolAddress: string) => boolean;
  shouldExposePublicPool?: (
    chainId: number,
    poolAddress: string,
    token0Address: string,
    token1Address: string
  ) => boolean;
  shouldUsePublicPoolForRouting?: (
    chainId: number,
    poolAddress: string,
    token0Address: string,
    token1Address: string
  ) => boolean;
  resolveConfigRef?: (ref: ConfigRef | undefined) => string;
}

function loadGruTransportLoader(): GruTransportLoader | null {
  const loader = loadTokenMappingLoader<GruTransportLoader>();
  if (loader?.getGruTransportMetadata && loader?.getActiveTransportPairs) {
    return loader;
  }
  return null;
}

export function getGruTransportMetadata(): GruTransportMetadata | null {
  const loader = loadGruTransportLoader();
  return loader?.getGruTransportMetadata?.() ?? null;
}

export function getGasAssetFamilies(): GruTransportGasAssetFamily[] {
  const loader = loadGruTransportLoader();
  return loader?.getGasAssetFamilies?.() ?? getGruTransportMetadata()?.gasAssetFamilies ?? [];
}

export function getGasRedeemGroups(): GruTransportGasRedeemGroup[] {
  const loader = loadGruTransportLoader();
  return loader?.getGasRedeemGroups?.() ?? getGruTransportMetadata()?.gasRedeemGroups ?? [];
}

export function getGasProtocolExposure(): GruTransportGasProtocolExposure[] {
  const loader = loadGruTransportLoader();
  return loader?.getGasProtocolExposure?.() ?? getGruTransportMetadata()?.gasProtocolExposure ?? [];
}

export function isGasRedemptionPathAllowed(
  fromChainId: number,
  toChainId: number,
  identifier: string
): boolean {
  const loader = loadGruTransportLoader();
  return loader?.isGasRedemptionPathAllowed?.(fromChainId, toChainId, identifier) ?? false;
}

export function getActiveTransportPairs(): GruTransportPair[] {
  const loader = loadGruTransportLoader();
  return loader?.getActiveTransportPairs?.() ?? [];
}

export function getActiveTransportPairBySymbol(
  fromChainId: number,
  toChainId: number,
  symbol: string
): GruTransportPair | null {
  const loader = loadGruTransportLoader();
  return loader?.getActiveTransportPair?.(fromChainId, toChainId, { symbol }) ?? null;
}

export function getApprovedBridgePeerByChain(chainId: number): GruTransportBridgePeer | null {
  const loader = loadGruTransportLoader();
  return loader?.getApprovedBridgePeer?.(chainId) ?? null;
}

export function resolveConfigRef(ref: ConfigRef | undefined): string {
  const loader = loadGruTransportLoader();
  return loader?.resolveConfigRef?.(ref) ?? '';
}

export function getActivePublicPools(): GruTransportPublicPool[] {
  const loader = loadGruTransportLoader();
  return loader?.getActivePublicPools?.() ?? [];
}

export function isPublicPoolActive(chainId: number, poolAddress: string): boolean {
  const loader = loadGruTransportLoader();
  return loader?.isPublicPoolActive?.(chainId, poolAddress) ?? true;
}

export function isPublicPoolRoutable(chainId: number, poolAddress: string): boolean {
  const loader = loadGruTransportLoader();
  return loader?.isPublicPoolRoutable?.(chainId, poolAddress) ?? true;
}

export function isPublicPoolMcpVisible(chainId: number, poolAddress: string): boolean {
  const loader = loadGruTransportLoader();
  return loader?.isPublicPoolMcpVisible?.(chainId, poolAddress) ?? false;
}

export function shouldExposePublicPool(
  chainId: number,
  poolAddress: string,
  token0Address: string,
  token1Address: string
): boolean {
  if (chainId === 138 && isChain138CanonicalLivePool(poolAddress)) return true;
  const loader = loadGruTransportLoader();
  return loader?.shouldExposePublicPool?.(chainId, poolAddress, token0Address, token1Address) ?? true;
}

export function shouldUsePublicPoolForRouting(
  chainId: number,
  poolAddress: string,
  token0Address: string,
  token1Address: string
): boolean {
  if (chainId === 138 && isChain138CanonicalLivePool(poolAddress)) return true;
  const loader = loadGruTransportLoader();
  return loader?.shouldUsePublicPoolForRouting?.(chainId, poolAddress, token0Address, token1Address) ?? true;
}

export function filterPoolsForExposure<T extends PublicPoolLike>(chainId: number, pools: T[]): T[] {
  return pools.filter((pool) =>
    shouldExposePublicPool(chainId, pool.poolAddress, pool.token0Address, pool.token1Address)
  );
}

export function filterPoolsForRouting<T extends PublicPoolLike>(chainId: number, pools: T[]): T[] {
  return pools.filter((pool) =>
    shouldUsePublicPoolForRouting(chainId, pool.poolAddress, pool.token0Address, pool.token1Address)
  );
}
