/**
 * Quote Aggregator Service
 * Aggregates quotes from multiple DEX protocols for price comparison
 */

import { ethers } from 'ethers';
import { SwapProvider, Quote } from './liquidity-engine.service';

export interface AggregatedQuote {
  provider: SwapProvider;
  amountOut: bigint;
  priceImpact: number;
  gasEstimate: bigint;
  effectiveOutput: bigint; // amountOut - gasCost
  timestamp: number;
}

export class QuoteAggregator {
  private provider: ethers.Provider;
  private enhancedSwapRouter: ethers.Contract;

  constructor(provider: ethers.Provider, enhancedSwapRouterAddress: string) {
    this.provider = provider;
    this.enhancedSwapRouter = new ethers.Contract(
      enhancedSwapRouterAddress,
      [], // ABI would go here
      provider
    );
  }

  /**
   * Aggregate quotes from all providers
   */
  async aggregateQuotes(
    inputToken: string,
    outputToken: string,
    amount: bigint
  ): Promise<AggregatedQuote[]> {
    try {
      const result = await this.enhancedSwapRouter.getQuotes(outputToken, amount);
      const providers = result[0] as SwapProvider[];
      const amounts = result[1] as bigint[];

      const quotes: AggregatedQuote[] = [];
      const gasPrice = await this.provider.getFeeData();

      for (let i = 0; i < providers.length; i++) {
        if (amounts[i] > 0n) {
          const gasEstimate = await this.estimateGasForProvider(providers[i]);
          const gasCost = gasEstimate * (gasPrice.gasPrice || 0n);
          const effectiveOutput = amounts[i] - gasCost;
          const priceImpact = this.calculatePriceImpact(amount, amounts[i]);

          quotes.push({
            provider: providers[i],
            amountOut: amounts[i],
            priceImpact,
            gasEstimate,
            effectiveOutput,
            timestamp: Date.now(),
          });
        }
      }

      // Sort by effective output (best first)
      return quotes.sort((a, b) => {
        if (a.effectiveOutput > b.effectiveOutput) return -1;
        if (a.effectiveOutput < b.effectiveOutput) return 1;
        return 0;
      });
    } catch (error) {
      console.error('Error aggregating quotes:', error);
      return [];
    }
  }

  /**
   * Get best quote (highest effective output)
   */
  async getBestQuote(
    inputToken: string,
    outputToken: string,
    amount: bigint
  ): Promise<AggregatedQuote | null> {
    const quotes = await this.aggregateQuotes(inputToken, outputToken, amount);
    return quotes.length > 0 ? quotes[0] : null;
  }

  /**
   * Compare quotes side by side
   */
  async compareQuotes(
    inputToken: string,
    outputToken: string,
    amount: bigint
  ): Promise<{
    best: AggregatedQuote | null;
    all: AggregatedQuote[];
    savings: Map<SwapProvider, bigint>; // Savings vs worst quote
  }> {
    const quotes = await this.aggregateQuotes(inputToken, outputToken, amount);
    
    if (quotes.length === 0) {
      return { best: null, all: [], savings: new Map() };
    }

    const best = quotes[0];
    const worst = quotes[quotes.length - 1];
    const savings = new Map<SwapProvider, bigint>();

    quotes.forEach(quote => {
      savings.set(quote.provider, quote.effectiveOutput - worst.effectiveOutput);
    });

    return { best, all: quotes, savings };
  }

  /**
   * Calculate price impact
   */
  private calculatePriceImpact(amountIn: bigint, amountOut: bigint): number {
    // Simplified - assumes 1:1 for stablecoins
    const expectedOut = amountIn;
    const impact = Number(amountOut - expectedOut) / Number(expectedOut) * 100;
    return Math.abs(impact);
  }

  /**
   * Estimate gas for provider
   */
  private async estimateGasForProvider(provider: SwapProvider): Promise<bigint> {
    const gasEstimates: Record<SwapProvider, bigint> = {
      [SwapProvider.UniswapV3]: 150000n,
      [SwapProvider.Curve]: 200000n,
      [SwapProvider.Dodoex]: 180000n,
      [SwapProvider.Balancer]: 220000n,
      [SwapProvider.OneInch]: 250000n,
    };

    return gasEstimates[provider] || 200000n;
  }
}

export default QuoteAggregator;

