
import { ethers } from 'ethers';
import { PAIR_ABI, PANCAKESWAP_ROUTER_ABI, CONTRACT_ADDRESSES } from './abis';
import { logger } from '../../utils/logger';

interface PriceHistoryItem {
  timestamp: number;
  usd: number;
  bnb: number;
}

let lastPrice = {
  usd: 0,
  bnb: 0,
  lastUpdated: 0,
  changePercent: 0
};

let priceHistory: PriceHistoryItem[] = [];

const EXATECH_DECIMALS = 9;

/**
 * 
 * @returns 
 */
export async function getTokenPrice() {
  try {
    const now = Date.now();
    if (lastPrice.lastUpdated > 0 && (now - lastPrice.lastUpdated < 1 * 60 * 1000)) {
      return {
        usd: lastPrice.usd,
        bnb: lastPrice.bnb,
        changePercent: lastPrice.changePercent
      };
    }

    const provider = new ethers.providers.JsonRpcProvider("https://bsc-dataseed1.binance.org");
    
    const bnbPriceInUsd = await getBnbPriceInUsd(provider);
    logger.debug(`BNB price in USD: ${bnbPriceInUsd}`);
    
    const bnbPerExatech = await getExatechPriceInBnbViaRouter(provider);
    logger.debug(`EXATECH price in BNB via Router: ${bnbPerExatech}`);
    
    const usdPrice = bnbPerExatech * bnbPriceInUsd;
    logger.debug(`EXATECH final price: ${usdPrice} USD`);
    
    priceHistory.push({
      timestamp: now,
      usd: usdPrice,
      bnb: bnbPerExatech
    });
    
    if (priceHistory.length > 24) {
      priceHistory.shift();
    }
    
    let changePercent = 0;
    if (priceHistory.length > 1) {
      const oldestPrice = priceHistory[0].usd;
      changePercent = ((usdPrice - oldestPrice) / oldestPrice) * 100;
    }
    
    lastPrice = {
      usd: usdPrice,
      bnb: bnbPerExatech,
      lastUpdated: now,
      changePercent
    };
    
    return {
      usd: usdPrice,
      bnb: bnbPerExatech,
      changePercent
    };
  } catch (error) {
    logger.error("Error fetching token price:", error);
    throw error; 
  }
}


async function getBnbPriceInUsd(provider: ethers.providers.Provider): Promise<number> {
  const router = new ethers.Contract(
    CONTRACT_ADDRESSES.PANCAKESWAP_ROUTER,
    PANCAKESWAP_ROUTER_ABI,
    provider
  );
  
  const oneBNB = ethers.utils.parseEther("1");
  const bnbToUsdt = await router.getAmountsOut(
    oneBNB,
    [CONTRACT_ADDRESSES.WBNB, CONTRACT_ADDRESSES.USDT]
  );
  
  return parseFloat(ethers.utils.formatUnits(bnbToUsdt[1], 18));
}


async function getExatechPriceInBnbViaRouter(provider: ethers.providers.Provider): Promise<number> {
  try {
    const router = new ethers.Contract(
      CONTRACT_ADDRESSES.PANCAKESWAP_ROUTER,
      PANCAKESWAP_ROUTER_ABI,
      provider
    );
    
    const exatechAmount = ethers.utils.parseUnits("1", EXATECH_DECIMALS); 
    
    logger.debug(`Getting price for: ${exatechAmount.toString()} EXATECH baseunits (1 EXATECH)`);
    
    const amountsOut = await router.getAmountsOut(
      exatechAmount,
      [CONTRACT_ADDRESSES.EXATECH, CONTRACT_ADDRESSES.WBNB]
    );
    
    const bnbAmount = parseFloat(ethers.utils.formatEther(amountsOut[1]));
    logger.debug(`Router estimates 1 EXATECH = ${bnbAmount} BNB`);
    
    return bnbAmount;
  } catch (error) {
    logger.error("Error getting price via router:", error);
    return getExatechPriceInBnbViaReserves(provider);
  }
}


async function getExatechPriceInBnbViaReserves(provider: ethers.providers.Provider): Promise<number> {
  try {
    logger.debug("Trying reserve method as fallback...");
    const pairContract = new ethers.Contract(
      CONTRACT_ADDRESSES.EXATECH_BNB_PAIR,
      PAIR_ABI,
      provider
    );
    
    const token0 = await pairContract.token0();
    const token1 = await pairContract.token1();
    
    logger.debug(`Pair tokens: token0=${token0}, token1=${token1}`);
    logger.debug(`EXATECH address: ${CONTRACT_ADDRESSES.EXATECH}`);
    
    const isExatechToken0 = token0.toLowerCase() === CONTRACT_ADDRESSES.EXATECH.toLowerCase();
    logger.debug(`Is EXATECH token0? ${isExatechToken0}`);
    
    const reserves = await pairContract.getReserves();
    
    let exatechReserve, bnbReserve;
    if (isExatechToken0) {
      exatechReserve = parseFloat(ethers.utils.formatUnits(reserves.reserve0, EXATECH_DECIMALS));
      bnbReserve = parseFloat(ethers.utils.formatEther(reserves.reserve1));
      logger.debug(`Reserves: EXATECH=${exatechReserve}, BNB=${bnbReserve}`);
    } else {
      exatechReserve = parseFloat(ethers.utils.formatUnits(reserves.reserve1, EXATECH_DECIMALS));
      bnbReserve = parseFloat(ethers.utils.formatEther(reserves.reserve0));
      logger.debug(`Reserves: BNB=${bnbReserve}, EXATECH=${exatechReserve}`);
    }
    
    logger.debug(`Exatech reserve: ${exatechReserve}, BNB reserve: ${bnbReserve}`);
    
    if (exatechReserve < 0.0001 || bnbReserve < 0.0001) {
      logger.error("Reserves too small for accurate calculation");
      throw new Error("Reserves too small");
    }
    
    return bnbReserve / exatechReserve;
  } catch (error) {
    logger.error("Error in reserve method:", error);
    throw error;
  }
}

/**
 * @returns 
 */
export async function getFormattedTokenPrice() {
  try {
    const price = await getTokenPrice();
    const priceStr = price.usd.toFixed(8);
    const changeStr = price.changePercent >= 0 
      ? `+${price.changePercent.toFixed(1)}%` 
      : `${price.changePercent.toFixed(1)}%`;
    
    return {
      price: `$${priceStr}`,
      change: changeStr,
      isPositive: price.changePercent >= 0
    };
  } catch (error) {
    logger.error("Error formatting token price:", error);
    return {
      price: "N/A", 
      change: "-", 
      isPositive: false
    };
  }
} 