using GraphQL;
using GraphQL.Client.Abstractions;
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Infrastructure.Evm.Abstractions;
using Managing.Infrastructure.Evm.Services;
using Managing.Infrastructure.Evm.Subgraphs.Models;
using NBitcoin;
using static Managing.Common.Enums;

namespace Managing.Infrastructure.Evm.Subgraphs;

public class Gbc : ISubgraphPrices
{
    private readonly IGraphQLClient _graphQLClient;
    public SubgraphProvider GetProvider() => SubgraphProvider.Gbc;

    public Gbc(IGraphQLClient graphQLHttpClient)
    {
        _graphQLClient = graphQLHttpClient ?? throw new ArgumentNullException(nameof(graphQLHttpClient));
    }

    public async Task<IEnumerable<Candle>> GetPrices(Ticker ticker, DateTime startDate, Timeframe timeframe)
    {
        var batchSize = 1000;
        var batchMax = 6;
        var priceRounds = new List<GbcPrice>();
        var tickerContract = TokenService.GetContractAddress(ticker);
        var unixTimeframe = timeframe.GetUnixInterval();
        var start = startDate.ToUnixTimestamp();
        var end = DateTime.UtcNow.ToUnixTimestamp();
        var feedCondition = $@"{{ tokenAddress: ""_{tickerContract}"", interval: ""_{unixTimeframe}"", timestamp_gte: {start}, timestamp_lte: {end} }}";

        // Fetching prices from graphql ticker
        for (int i = 0; i < batchMax; i++)
        {
            var query = $"{{ pricefeeds(first: {batchSize}, skip: {i * batchSize}, orderBy: timestamp, orderDirection: desc, where: {feedCondition} ) {{ timestamp,o,h,l,c}} }}";
            var graphQuery = new GraphQLRequest
            {
                Query = query
            };

            var response = await _graphQLClient.SendQueryAsync<GbcPrices>(graphQuery);
            priceRounds.AddRange(response.Data.PriceFeeds);
        }

        priceRounds.Sort((timeA, timeB) => timeA.Timestamp - timeB.Timestamp);

        var candles = new List<Candle>();

        var firstRound = priceRounds.FirstOrDefault();
        if (firstRound == null)
            return candles;

        var previousCandle = BuildCandle(firstRound, ticker, timeframe);

        // Format response
        foreach (var price in priceRounds.Skip(1))
        {
            var candle = BuildCandle(price, ticker, timeframe);
            candle.OpenTime = previousCandle.Date;
            candles.Add(candle);
        }

        return candles;
    }

    private Candle BuildCandle(GbcPrice ohlc, Ticker ticker, Timeframe timeframe)
    {
        return new Candle()
        {
            Date = DateHelpers.GetFromUnixTimestamp(ohlc.Timestamp),
            Open = FormatPrice(ohlc.O),
            High = FormatPrice(ohlc.H),
            Low = FormatPrice(ohlc.L),
            Close = FormatPrice(ohlc.C),
            Exchange = TradingExchanges.Evm,
            Ticker = ticker.ToString(),
            Timeframe = timeframe
        };
    }

    private static decimal FormatPrice(string price)
    {
        return (decimal)(double.Parse(price) / 1e30);
    }

    public Task<decimal> GetVolume(Ticker ticker)
    {
        throw new NotImplementedException();
    }

    public Task<IEnumerable<Ticker>> GetTickers()
    {
        var tickers = new List<Ticker>() { 
            Ticker.BTC,
            Ticker.LINK,
            Ticker.ETH,
            Ticker.UNI
        };

        return Task.FromResult(tickers.AsEnumerable());
    }
}
