import { Router, Request, Response } from 'express';
import { PoolRepository } from '../../database/repositories/pool-repo';
import { cacheMiddleware } from '../middleware/cache';
import { logger } from '../../utils/logger';

const router: Router = Router();
const poolRepo = new PoolRepository();

/**
 * Uniswap V2-style constant-product quote: amountOut = (reserveOut * amountIn * 997) / (reserveIn * 1000 + amountIn * 997)
 */
function quoteAmountOut(
  amountIn: bigint,
  reserveIn: bigint,
  reserveOut: bigint
): bigint {
  if (reserveIn === BigInt(0)) {
    return BigInt(0);
  }
  const amountInWithFee = amountIn * BigInt(997);
  const numerator = reserveOut * amountInWithFee;
  const denominator = reserveIn * BigInt(1000) + amountInWithFee;
  return numerator / denominator;
}

/**
 * GET /api/v1/quote
 * Returns an estimated amountOut for a token swap (constant-product from first available pool).
 * Query: chainId, tokenIn, tokenOut, amountIn (raw amount in token's smallest unit).
 */
router.get(
  '/quote',
  cacheMiddleware(60 * 1000),
  async (req: Request, res: Response) => {
    try {
      const chainId = parseInt(req.query.chainId as string, 10);
      const tokenIn = (req.query.tokenIn as string)?.toLowerCase();
      const tokenOut = (req.query.tokenOut as string)?.toLowerCase();
      const amountInRaw = req.query.amountIn as string;

      if (!chainId || !tokenIn || !tokenOut || amountInRaw === undefined) {
        return res.status(400).json({
          error: 'Missing required query: chainId, tokenIn, tokenOut, amountIn',
          amountOut: null,
        });
      }

      let amountIn: bigint;
      try {
        amountIn = BigInt(amountInRaw);
      } catch {
        return res.status(400).json({
          error: 'Invalid amountIn (must be integer string)',
          amountOut: null,
        });
      }

      if (tokenIn === tokenOut) {
        return res.json({ amountOut: amountInRaw, poolAddress: null });
      }

      const pools = await poolRepo.getPoolsByToken(chainId, tokenIn);
      const pairPools = pools.filter(
        (p) =>
          p.token0Address.toLowerCase() === tokenOut ||
          p.token1Address.toLowerCase() === tokenOut
      );

      if (pairPools.length === 0) {
        return res.json({
          amountOut: null,
          error: 'No pool found for this token pair',
          poolAddress: null,
        });
      }

      let bestAmountOut = BigInt(0);
      let bestPool = pairPools[0];

      for (const pool of pairPools) {
        const reserveIn =
          pool.token0Address.toLowerCase() === tokenIn
            ? BigInt(pool.reserve0)
            : BigInt(pool.reserve1);
        const reserveOut =
          pool.token0Address.toLowerCase() === tokenOut
            ? BigInt(pool.reserve0)
            : BigInt(pool.reserve1);
        const out = quoteAmountOut(amountIn, reserveIn, reserveOut);
        if (out > bestAmountOut) {
          bestAmountOut = out;
          bestPool = pool;
        }
      }

      res.json({
        amountOut: bestAmountOut.toString(),
        poolAddress: bestPool.poolAddress,
        dexType: bestPool.dexType,
      });
    } catch (error) {
      logger.error('Quote error:', error);
      res.status(500).json({
        error: error instanceof Error ? error.message : 'Internal server error',
        amountOut: null,
      });
    }
  }
);

export default router;
