/**
 * Route Planner - Chain138 to Tezos USDtz
 */

import { validateTezosAddress } from '../../../shared/utils/tezos-address';
import {
  getCandidateBridgesForPlanning,
  CHAIN_138,
  CHAIN_ETHEREUM,
  CHAIN_TEZOS,
  CHAIN_ALL_MAINNET,
} from './bridge-capability-matrix';
import { getChain138SwapQuote } from './chain138-quote.service';
import { getCCIPFeeEstimate } from './ccip-fee.service';
import { getTezosDexQuote } from './tezos-dex-quote.service';
import { validateRouteAllowlist } from './allowlist.config';

export type ChainLabel = 'CHAIN138' | 'ALL_MAINNET' | 'HUB_EVM' | 'TEZOS';
export type HopAction = 'SWAP' | 'BRIDGE' | 'MINT' | 'REDEEM' | 'TRANSFER';

export interface RouteHop {
  chain: ChainLabel;
  action: HopAction;
  protocol: string;
  asset_in: string;
  amount_in: string;
  asset_out: string;
  min_amount_out: string;
  estimated_fees: string;
  deadline?: number;
}

export interface RoutePlan {
  route_id: string;
  hops: RouteHop[];
  totalEstimatedFees: string;
  estimatedTimeSeconds: number;
  riskScore?: number;
}

export interface RoutePlanRequest {
  source_chain_id: number;
  source_asset: string;
  source_amount: string;
  destination_tezos_address: string;
  max_slippage_bps?: number;
  max_total_fees?: string;
  prefer_non_custodial?: boolean;
}

export interface RoutePlanResult {
  valid: boolean;
  error?: string;
  routes?: RoutePlan[];
}

const CUSDC = '0xf22258f57794CC8E06237084b353Ab30fFfa640b';
const AUSDC = '0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881'; // USDC on ALL Mainnet (651940)
const AUSDT = '0x015B1897Ed5279930bC2Be46F661894d219292A6'; // AUSDT primary on ALL Mainnet (651940)

/** Bridgeable stables on ALL Mainnet */
const ALL_MAINNET_STABLES = [AUSDC, AUSDT] as const;

const SUPPORTED_SOURCE_CHAINS = [CHAIN_138, CHAIN_ALL_MAINNET] as const;

export function planRoutes(request: RoutePlanRequest): RoutePlanResult {
  if (!SUPPORTED_SOURCE_CHAINS.includes(request.source_chain_id as (typeof SUPPORTED_SOURCE_CHAINS)[number])) {
    return { valid: false, error: 'Only source_chain_id=138 (Chain138) or 651940 (ALL Mainnet) is supported' };
  }
  const addr = validateTezosAddress(request.destination_tezos_address);
  if (!addr.valid) return { valid: false, error: addr.error };
  const amount = BigInt(request.source_amount);
  if (amount <= 0n) return { valid: false, error: 'source_amount must be > 0' };

  const sourceChain = request.source_chain_id;
  const isChain138 = sourceChain === CHAIN_138;
  const isAllMainnet = sourceChain === CHAIN_ALL_MAINNET;

  const srcToEth = getCandidateBridgesForPlanning(sourceChain, CHAIN_ETHEREUM);
  const eth2tz = getCandidateBridgesForPlanning(CHAIN_ETHEREUM, CHAIN_TEZOS, 'USDC');
  if (srcToEth.length === 0) return { valid: false, error: `No bridge from source chain ${sourceChain} to Ethereum` };

  const routeId = `route-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
  const provider = eth2tz[0]?.provider ?? 'Wrap Protocol';
  const srcBridge = srcToEth[0]!;
  const sourceLabel = isChain138 ? 'CHAIN138' : 'ALL_MAINNET';
  const srcLower = request.source_asset.toLowerCase();
  const stableOut = isChain138
    ? CUSDC
    : ALL_MAINNET_STABLES.some((a) => a.toLowerCase() === srcLower)
      ? request.source_asset
      : AUSDC;
  const needsSwap = request.source_asset.toLowerCase() !== stableOut.toLowerCase();

  const hops: RouteHop[] = [
    {
      chain: sourceLabel,
      action: needsSwap ? 'SWAP' : 'TRANSFER',
      protocol: needsSwap ? (isChain138 ? 'EnhancedSwapRouter' : 'AlltraDEX') : 'Direct',
      asset_in: request.source_asset,
      amount_in: request.source_amount,
      asset_out: stableOut,
      min_amount_out: request.source_amount,
      estimated_fees: '0',
    },
    {
      chain: sourceLabel,
      action: 'BRIDGE',
      protocol: srcBridge.provider,
      asset_in: stableOut,
      amount_in: request.source_amount,
      asset_out: stableOut.includes('USDT') ? 'USDT' : 'USDC',
      min_amount_out: request.source_amount,
      estimated_fees: '0',
    },
    { chain: 'HUB_EVM', action: 'BRIDGE', protocol, asset_in: 'USDC', amount_in: request.source_amount, asset_out: 'USDC', min_amount_out: request.source_amount, estimated_fees: '0' },
    { chain: 'TEZOS', action: 'SWAP', protocol: 'Plenty', asset_in: 'USDC', amount_in: request.source_amount, asset_out: 'USDtz', min_amount_out: request.source_amount, estimated_fees: '0' },
  ];

  const allowlistErrors = validateRouteAllowlist(
    hops.map((h) => ({ protocol: h.protocol, chain: h.chain, asset_in: h.asset_in }))
  );
  if (allowlistErrors.length > 0) return { valid: false, error: allowlistErrors.join('; ') };

  return { valid: true, routes: [{ route_id: routeId, hops, totalEstimatedFees: '0', estimatedTimeSeconds: 1800 }] };
}

/** Async route planning with real quotes and fee estimates */
export async function planRoutesAsync(request: RoutePlanRequest): Promise<RoutePlanResult> {
  const sync = planRoutes(request);
  if (!sync.valid || !sync.routes?.length) return sync;

  const [route] = sync.routes;
  const hops = [...route.hops];
  let totalFees = 0n;
  const sourceChain = request.source_chain_id;
  const isChain138 = sourceChain === CHAIN_138;
  const stableOut = hops[0]!.asset_out;
  const eth2tz = getCandidateBridgesForPlanning(CHAIN_ETHEREUM, CHAIN_TEZOS, 'USDC')[0];
  const provider = eth2tz?.provider ?? 'Wrap Protocol';

  if (hops[0]!.action === 'SWAP' && request.source_asset.toLowerCase() !== stableOut.toLowerCase()) {
    if (isChain138) {
      const q = await getChain138SwapQuote(request.source_asset, stableOut, request.source_amount);
      hops[0] = { ...hops[0]!, min_amount_out: q.amountOutMin, estimated_fees: '0' };
    } else {
      hops[0] = { ...hops[0]!, min_amount_out: request.source_amount, estimated_fees: '0' };
    }
  }

  if (isChain138) {
    const ccipFee = await getCCIPFeeEstimate(CHAIN_138, CHAIN_ETHEREUM, CUSDC, request.source_amount);
    totalFees += BigInt(ccipFee.feeLink);
    hops[1] = { ...hops[1]!, estimated_fees: ccipFee.feeLink };
  } else {
    hops[1] = { ...hops[1]!, estimated_fees: '0' };
  }

  hops[2] = { ...hops[2]!, protocol: provider, estimated_fees: '0' };

  const hubStable = stableOut.toLowerCase().includes('usdt') ? 'USDT' : 'USDC';
  const tezosQuote = await getTezosDexQuote({
    tokenIn: hubStable,
    tokenOut: USDTZ_CONTRACT,
    amountIn: request.source_amount,
    slippageBps: request.max_slippage_bps ?? 50,
  });
  if (tezosQuote) {
    hops[3] = {
      ...hops[3]!,
      min_amount_out: tezosQuote.amountOutMin,
      estimated_fees: '0',
    };
  }

  return {
    valid: true,
    routes: [
      {
        ...route,
        hops,
        totalEstimatedFees: totalFees.toString(),
        estimatedTimeSeconds: 1800,
      },
    ],
  };
}

const USDTZ_CONTRACT = 'KT1T87QbpXEVgkwsNPzz8iRoah3SS3D1MDmh';
