const axios = require('axios');
const { Coin } = require('../models'); // Adjust path as needed

class ConversionsController {
  constructor() {
    this.bridge = 'USD';
  }

  /**
   * Update all rates (crypto + forex)
   */
  async updateRates(req, res) {
    try {
      // 1️⃣ Load active coins
      const coins = await Coin.findAll({
        where: { status: 'ACTIVE' },
        attributes: ['symbol']
      });

      const coinSymbols = coins.map(c => c.symbol);

      const fiatCurrencies = Object.keys(
        require('../config/currencies').FIAT || {}
      );

      const allCrypto = [...fiatCurrencies, ...coinSymbols];

      const majorCoins =
        require('../config/currencies').major_coins || [];

      const cryptoPrices = {};

      // 2️⃣ Fetch crypto prices
      for (const coin of allCrypto) {
        if (coin === this.bridge) {
          cryptoPrices[coin] = 1;
          continue;
        }

        let price = null;

        // Binance for major coins
        if (majorCoins.includes(coin)) {
          price = await this.fetchBinancePrice(coin, this.bridge);
        }

        // Coinbase
        if (!price && this.bridge === 'USD') {
          price = await this.fetchCoinbasePrice(coin, 'USD');
        }

        // CoinGecko fallback
        if (!price) {
          price = await this.fetchCoinGeckoPrice(coin, this.bridge);
        }

        if (price) {
          cryptoPrices[coin] = price;
        }
      }

      // 3️⃣ Store crypto pair rates
      for (const from of allCrypto) {
        for (const to of allCrypto) {
          if (from === to) continue;
          if (!cryptoPrices[from] || !cryptoPrices[to]) continue;

          const rate = cryptoPrices[from] / cryptoPrices[to];

          const existing = await Rate.findOne({
            where: { from_currency: from, to_currency: to }
          });

          if (existing) {
            existing.previous_rate = existing.rate;
            existing.rate = rate;
            existing.last_updated = new Date();
            await existing.save();
          } else {
            await Rate.create({
              from_currency: from,
              to_currency: to,
              rate,
              previous_rate: null,
              last_updated: new Date()
            });
          }
        }
      }

      // 4️⃣ Forex Pairs
      const forexPairs = [
        { from: 'USD', to: 'EUR' },
        { from: 'USD', to: 'ZAR' },
        { from: 'EUR', to: 'GBP' },
        { from: 'GBP', to: 'JPY' }
      ];

      for (const pair of forexPairs) {
        const rate = await this.fetchForexRate(pair.from, pair.to);

        if (!rate) continue;

        const existing = await Rate.findOne({
          where: {
            from_currency: pair.from,
            to_currency: pair.to
          }
        });

        if (existing) {
          existing.previous_rate = existing.rate;
          existing.rate = rate;
          existing.last_updated = new Date();
          await existing.save();
        } else {
          await Rate.create({
            from_currency: pair.from,
            to_currency: pair.to,
            rate,
            previous_rate: null,
            last_updated: new Date()
          });
        }
      }

      return res.json({
        status: 'success',
        message: 'Crypto and forex rates updated successfully.'
      });

    } catch (error) {
      console.error(error);
      return res.status(500).json({
        status: 'error',
        message: 'Rate update failed'
      });
    }
  }

  /**
   * Convert currency
   */
  async convert(req, res) {
    try {
      const { from, to, amount } = req.body;

      if (!from || !to || !amount) {
        return res.status(400).json({
          status: 'error',
          message: 'Invalid parameters'
        });
      }

      const rate = await Rate.findOne({
        where: {
          from_currency: from.toUpperCase(),
          to_currency: to.toUpperCase()
        }
      });

      if (!rate) {
        return res.status(404).json({
          status: 'error',
          message: 'Conversion rate not found.'
        });
      }

      const convertedAmount = amount * rate.rate;

      return res.json({
        status: 'success',
        from: from.toUpperCase(),
        to: to.toUpperCase(),
        amount,
        converted_amount: convertedAmount,
        rate: rate.rate
      });

    } catch (error) {
      console.error(error);
      return res.status(500).json({
        status: 'error',
        message: 'Conversion failed'
      });
    }
  }

  // ----------------- PRICE SOURCES -----------------

  async fetchBinancePrice(coin, bridge) {
    try {
      const symbol = `${coin}${bridge}`;
      const { data } = await axios.get(
        `https://api.binance.com/api/v3/ticker/price?symbol=${symbol}`,
        { timeout: 5000 }
      );
      return data;
    } catch {
      return null;
    }
  }

  async fetchCoinGeckoPrice(coin, bridge) {
    try {
      const mapping = {
        BTC: 'bitcoin',
        ETH: 'ethereum',
        LTC: 'litecoin',
        USDT: 'tether',
        XRP: 'ripple',
        ADA: 'cardano',
        DOGE: 'dogecoin'
      };

      if (!mapping[coin]) return null;

      const id = mapping[coin];
      const vs = bridge.toLowerCase();

      const { data } = await axios.get(
        `https://api.coingecko.com/api/v3/simple/price?ids=${id}&vs_currencies=${vs}`,
        { timeout: 5000 }
      );

      return parseFloat(data[id][vs]);
    } catch {
      return null;
    }
  }

  async fetchCoinbasePrice(coin, bridge = 'USD') {
    try {
      const symbol = `${coin}-${bridge}`;
      const { data } = await axios.get(
        `https://api.coinbase.com/v2/prices/${symbol}/spot`,
        { timeout: 5000 }
      );
      return parseFloat(data.data.amount);
    } catch {
      return null;
    }
  }

  async fetchForexRate(from, to) {
    try {
      const { data } = await axios.get(
        `https://api.exchangerate.host/convert?from=${from}&to=${to}`,
        { timeout: 5000 }
      );
      return parseFloat(data.info.rate);
    } catch {
      return null;
    }
  }
}

module.exports = new ConversionsController();
