const axios = require('axios');
const { Op, fn, col, literal } = require('sequelize');
const { Coin, Price1m, Asset, SymbolStats, CoinMarket, Network, MarketPair } = require('../models');
const { mapPrice } = require('../utils/helpers');
const { ConflictError } = require('../utils/errors');
const walletService = require('./walletService');

const polygonApiKey = "spoIbZfKQePYO7e69gcKYsGgbtxReGmI";
const twelveApiKey = "dc019a4f664942fead52f8349a1dc8be";

let validBinanceSymbols = null;

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

    async getPairs(type, limit = null) {
        return await MarketPair.findAll({
            where: { type },
            ...(limit && { limit }),
            include: [
                { model: Asset, as: 'base',  required: true },
                { model: Asset, as: 'quote', required: true }
            ]
        });
    }
    
    async updateMarketCron() {
        // 1. Fetch external rates
        const response = await axios.get('http://localhost:5001/api/v1/latest/rates/all');
        const rates = response?.data?.data?.rates ?? [];

        // 2. Get assets from DB
        const assets = await MarketPair.findAll({
            where: { type: ['stock','fx','commodity'] }
        });

        // 3. Convert rates to map for fast lookup
        const ratesMap = {};
        for (const r of rates) {
            ratesMap[r.symbol] = r;
        }

        // 4. Build market pairs
        const marketPairs = assets
            .map(asset => {
                const rate = ratesMap[asset.symbol];

                if (!rate) return null; // skip if no match

                return {
                    asset_id: asset.id,
                    current_price: rate.price,
                    high24h: rate.high24h,
                    low24h: rate.low24h,
                    open24h: rate.open24h,
                    volume24h: rate.volume24h,
                    change24h: rate.change24h,
                    change_percent24h: rate.changePercent,
                };
            })
            .filter(Boolean); // remove nulls

        // 5. Insert / update
        await SymbolStats.bulkCreate(marketPairs, {
            updateOnDuplicate: [
                'current_price',
                'high24h',
                'low24h',
                'open24h',
                'volume24h',
                'change24h',
                'change_percent24h'
            ]
        });

        return {
            totalAssets: assets.length,
            updated: marketPairs.length
        };
    }

    async getCryptoPrices() {
        const pairs = await this.getPairs('spot');
        const seen = new Map();
        const uniqueSymbols = [];
        const pairMap = {};

        pairs.forEach(t => {
            if (!t.base || !t.quote) return;                          // ✅ correct guard
            const symbol = `${t.base.symbol}${t.quote.symbol}`.toUpperCase();
            if (!seen.has(symbol)) {
                pairMap[symbol] = t;
                uniqueSymbols.push(symbol);
                seen.set(symbol, true);
            }
        });

        const result = await this.fetchBinancePrice(uniqueSymbols);

        if (!result || !result.data) {
            console.error('No price data returned from Binance');
            return { uniqueSymbols, prices: null };
        }

        return { uniqueSymbols, prices: result.data };                // ✅ removed undefined payload
    }

    async updateCryptoPrices() {
        const pairs = await this.getPairs('spot');
        const seen = new Map();
        const uniqueSymbols = [];
        const pairMap = {};

        pairs.forEach(t => {
            if (!t.base || !t.quote) return;                          // ✅ fixed guard
            const symbol = `${t.base.symbol}${t.quote.symbol}`.toUpperCase();
            if (!seen.has(symbol)) {
                pairMap[symbol] = t;
                uniqueSymbols.push(symbol);
                seen.set(symbol, true);
            }
        });

        const result = await this.fetchBinancePrice(uniqueSymbols);

        if (!result || !result.data) {
            console.error('No price data returned from Binance');
            return { uniqueSymbols, prices: null };
        }

        const now = new Date();

        const payload = result.data.map(p => {
            const pair = pairMap[p.symbol];
            if (!pair) return null;

            return {
                asset_id:          pair.id,                      // ✅ base asset id not pair id
                current_price:     p.lastPrice,
                open24h:           p.openPrice,
                high24h:           p.highPrice,
                low24h:            p.lowPrice,
                volume24h:         p.quoteVolume,
                change24h:         p.priceChange,
                change_percent24h: p.priceChangePercent,
                timestamp:         now
            };
        }).filter(Boolean);

        await SymbolStats.bulkCreate(payload, {
            updateOnDuplicate: [
                'current_price',
                'open24h',
                'high24h',
                'low24h',
                'volume24h',
                'change24h',
                'change_percent24h',
                'timestamp'
            ]
        });

        return { uniqueSymbols, prices: result.data, payload };
    }

    async fetchBinanceSymbols() {
        if (validBinanceSymbols) return validBinanceSymbols;

        try {
            const { data } = await axios.get(
                `https://api.binance.us/api/v3/exchangeInfo`,
                { timeout: 5000 }
            );
            validBinanceSymbols = new Set(data.symbols.map(s => s.symbol));
            console.log(`Loaded ${validBinanceSymbols.size} Binance.US symbols`);
            return validBinanceSymbols;
        } catch (error) {
            console.error(`Failed to fetch Binance exchange info:`, error.message);
            return new Set();
        }
    }

    async fetchBinancePrice(symbols) {
        try {
            const validSymbols = await this.fetchBinanceSymbols();

            const validatedSymbols = symbols.filter(s => validSymbols.has(s));
            const rejectedSymbols  = symbols.filter(s => !validSymbols.has(s));

            if (rejectedSymbols.length > 0) {
                console.warn(`Symbols not on Binance.US:`, rejectedSymbols);
            }

            if (validatedSymbols.length === 0) {
                console.error(`No valid symbols found`);
                return null;
            }

            const response = await axios.get(
                `https://api.binance.us/api/v3/ticker/24hr`,
                {
                    params: { symbols: JSON.stringify(validatedSymbols) },
                    timeout: 5000
                }
            );

            return {
                data:     response.data,
                rejected: rejectedSymbols
            };

        } catch (error) {
            console.error(`Binance error:`, error.response?.data || error.message);
            return null;
        }
    }
}

module.exports = new ConversionService();