import fs from 'fs';
import path from 'path';

export type AggregatorFamily = '1inch' | '0x' | 'LiFi';
export type RouteStatus = 'live' | 'planned' | 'blocked';
export type RouteType = 'swap' | 'bridge' | 'swap-bridge-swap';

export interface AggregatorRouteLeg {
  kind: string;
  protocol?: string;
  executor?: string;
  executorAddress?: string;
  poolAddress?: string;
  tokenInAddress?: string;
  tokenOutAddress?: string;
  reserves?: Record<string, string>;
}

export interface LiveAggregatorRoute {
  routeId: string;
  status: 'live';
  aggregatorFamilies: AggregatorFamily[];
  fromChainId: number;
  toChainId: number;
  tokenInSymbol?: string;
  tokenInAddress?: string;
  tokenOutSymbol?: string;
  tokenOutAddress?: string;
  assetSymbol?: string;
  assetAddress?: string;
  routeType: 'swap' | 'bridge';
  hopCount?: number;
  bridgeType?: string;
  bridgeAddress?: string;
  label?: string;
  intermediateSymbols?: string[];
  legs?: AggregatorRouteLeg[];
  tags?: string[];
  notes?: string[];
}

export interface NonLiveAggregatorRoute {
  routeId: string;
  status: Exclude<RouteStatus, 'live'>;
  fromChainId: number;
  toChainId: number;
  routeType: RouteType;
  reason: string;
  tokenInSymbols?: string[];
}

export interface AggregatorRouteMatrix {
  $schema?: string;
  description?: string;
  version: string;
  updated: string;
  homeChainId: number;
  metadata?: {
    generatedFrom?: string[];
    verification?: {
      verifiedAt?: string;
      verifiedBy?: string;
      rpc?: string;
    };
    adapterNotes?: string[];
  };
  chains?: Record<string, unknown>;
  tokens?: Record<string, unknown>;
  liveSwapRoutes: LiveAggregatorRoute[];
  liveBridgeRoutes: LiveAggregatorRoute[];
  blockedOrPlannedRoutes: NonLiveAggregatorRoute[];
}

let cachedMatrix: AggregatorRouteMatrix | null = null;
let cachedPath: string | null = null;

function candidatePaths(): string[] {
  return [
    path.resolve(process.cwd(), '../../../config/aggregator-route-matrix.json'),
    path.resolve(process.cwd(), '../../config/aggregator-route-matrix.json'),
    path.resolve(__dirname, '../../../../../../config/aggregator-route-matrix.json'),
    path.resolve(__dirname, '../../../../../config/aggregator-route-matrix.json'),
  ];
}

export function resolveAggregatorRouteMatrixPath(): string | null {
  for (const candidate of candidatePaths()) {
    if (fs.existsSync(candidate)) {
      return candidate;
    }
  }
  return null;
}

export function loadAggregatorRouteMatrix(forceReload = false): AggregatorRouteMatrix | null {
  if (cachedMatrix && !forceReload) {
    return cachedMatrix;
  }

  const matrixPath = resolveAggregatorRouteMatrixPath();
  if (!matrixPath) {
    return null;
  }

  const raw = fs.readFileSync(matrixPath, 'utf8');
  cachedMatrix = JSON.parse(raw) as AggregatorRouteMatrix;
  cachedPath = matrixPath;
  return cachedMatrix;
}

export function getAggregatorRouteMatrixPath(): string | null {
  if (cachedPath) {
    return cachedPath;
  }
  return resolveAggregatorRouteMatrixPath();
}

export interface AggregatorRouteFilters {
  family?: string;
  fromChainId?: number;
  toChainId?: number;
  routeType?: string;
  tokenIn?: string;
  tokenOut?: string;
}

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

function normalizeFamily(value?: string): AggregatorFamily | undefined {
  if (!value) return undefined;
  const normalized = value.trim().toLowerCase();
  if (normalized === '1inch') return '1inch';
  if (normalized === '0x' || normalized === 'zeroex' || normalized === '0xapi') return '0x';
  if (normalized === 'lifi') return 'LiFi';
  return undefined;
}

export function filterLiveAggregatorRoutes(
  routes: LiveAggregatorRoute[],
  filters: AggregatorRouteFilters
): LiveAggregatorRoute[] {
  const family = normalizeFamily(filters.family);
  const tokenIn = normalizeAddress(filters.tokenIn);
  const tokenOut = normalizeAddress(filters.tokenOut);

  return routes.filter((route) => {
    if (family && !route.aggregatorFamilies.includes(family)) return false;
    if (filters.fromChainId && route.fromChainId !== filters.fromChainId) return false;
    if (filters.toChainId && route.toChainId !== filters.toChainId) return false;
    if (filters.routeType && route.routeType !== filters.routeType) return false;
    if (tokenIn && normalizeAddress(route.tokenInAddress) !== tokenIn && normalizeAddress(route.assetAddress) !== tokenIn) return false;
    if (tokenOut && normalizeAddress(route.tokenOutAddress) !== tokenOut && normalizeAddress(route.assetAddress) !== tokenOut) return false;
    return true;
  });
}

