/**
 * Arbitrage opportunities: triangular cycles with expected PnL.
 * Enumerates same-chain (138), hub-edge-hub, and 3-chain cycles per
 * global_arbitrage_engine_full_architecture.md and ultra_advanced_global_arbitrage_engine_blueprint.md.
 */

import { PoolRepository } from '../database/repositories/pool-repo';
import { TokenRepository } from '../database/repositories/token-repo';
import { getRoutesList, getChainIds } from '../config/heatmap-chains';
import { filterPoolsForRouting, getActiveTransportPairs } from '../config/gru-transport';

export interface ArbitrageOpportunity {
  cycleId: string;
  description: string;
  expectedPnlUsd: number;
  riskScore: number;
  capitalRequiredUsd: number;
  legs: { chainId: number; action: string; asset: string }[];
}

const poolRepo = new PoolRepository();
const tokenRepo = new TokenRepository();

const HUB_CHAIN = 138;
const EDGE_CHAINS = Array.from(
  new Set(
    getActiveTransportPairs()
      .map((pair) => pair.destinationChainId)
      .filter((chainId) => chainId !== HUB_CHAIN && chainId !== 651940)
  )
);

/** Same-chain triangle on 138: e.g. cUSDT -> cUSDC -> cUSDT via two pools. */
async function getSameChainCycles(): Promise<ArbitrageOpportunity[]> {
  const out: ArbitrageOpportunity[] = [];
  const pools = filterPoolsForRouting(HUB_CHAIN, await poolRepo.getPoolsByChain(HUB_CHAIN, 100));
  for (const p of pools) {
    const t0 = await tokenRepo.getToken(HUB_CHAIN, p.token0Address);
    const t1 = await tokenRepo.getToken(HUB_CHAIN, p.token1Address);
    const sym0 = t0?.symbol || '';
    const sym1 = t1?.symbol || '';
    if (!sym0 || !sym1) continue;
    const capital = p.totalLiquidityUsd ? Math.min(10000, p.totalLiquidityUsd * 0.01) : 10000;
    out.push({
      cycleId: `138:${sym0}-${sym1}-${sym0}`,
      description: `Same-chain triangle ${sym0} → ${sym1} → ${sym0} (Chain 138)`,
      expectedPnlUsd: 0,
      riskScore: 0.2,
      capitalRequiredUsd: capital,
      legs: [
        { chainId: HUB_CHAIN, action: 'swap', asset: sym0 },
        { chainId: HUB_CHAIN, action: 'swap', asset: sym1 },
        { chainId: HUB_CHAIN, action: 'swap', asset: sym0 },
      ],
    });
  }
  return out;
}

/** Hub-edge-hub: 138 -> edge -> 138 (SBS). */
function getHubEdgeHubCycles(): ArbitrageOpportunity[] {
  const out: ArbitrageOpportunity[] = [];
  const activeDestinations = new Set(EDGE_CHAINS);
  const routes = getRoutesList().filter((route) => activeDestinations.has(route.toChainId));
  const hubOut = routes.filter((r) => r.fromChainId === HUB_CHAIN && r.toChainId !== HUB_CHAIN);
  for (const r of hubOut.slice(0, 5)) {
    out.push({
      cycleId: `SBS:138-${r.toChainId}-138`,
      description: `Hub-edge-hub: 138 → ${r.toChainId} → 138 (swap-bridge-swap)`,
      expectedPnlUsd: 0,
      riskScore: 0.5,
      capitalRequiredUsd: 20000,
      legs: [
        { chainId: HUB_CHAIN, action: 'swap', asset: 'cUSDT' },
        { chainId: HUB_CHAIN, action: 'bridge', asset: 'WETH' },
        { chainId: r.toChainId, action: 'swap', asset: 'USDC' },
        { chainId: r.toChainId, action: 'bridge', asset: 'WETH' },
        { chainId: HUB_CHAIN, action: 'swap', asset: 'cUSDT' },
      ],
    });
  }
  return out;
}

/** 3-chain triangle: 138 -> A -> B -> 138. */
function getThreeChainCycles(): ArbitrageOpportunity[] {
  const out: ArbitrageOpportunity[] = [];
  const edges = EDGE_CHAINS.slice(0, 3);
  if (edges.length >= 2) {
    out.push({
      cycleId: `3chain:138-${edges[0]}-${edges[1]}-138`,
      description: `3-chain triangle 138 → ${edges[0]} → ${edges[1]} → 138`,
      expectedPnlUsd: 0,
      riskScore: 0.7,
      capitalRequiredUsd: 50000,
      legs: [
        { chainId: HUB_CHAIN, action: 'swap', asset: 'cUSDT' },
        { chainId: HUB_CHAIN, action: 'bridge', asset: 'WETH' },
        { chainId: edges[0], action: 'swap', asset: 'USDC' },
        { chainId: edges[0], action: 'bridge', asset: 'WETH' },
        { chainId: edges[1], action: 'swap', asset: 'USDT' },
        { chainId: edges[1], action: 'bridge', asset: 'WETH' },
        { chainId: HUB_CHAIN, action: 'swap', asset: 'cUSDT' },
      ],
    });
  }
  return out;
}

export async function getArbitrageOpportunities(): Promise<ArbitrageOpportunity[]> {
  const [sameChain, hubEdgeHub, threeChain] = await Promise.all([
    getSameChainCycles(),
    Promise.resolve(getHubEdgeHubCycles()),
    Promise.resolve(getThreeChainCycles()),
  ]);
  return [...sameChain, ...hubEdgeHub, ...threeChain];
}
