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

namespace Managing.Infrastructure.Evm.Subgraphs;

public class Chainlink : ISubgraphPrices
{
    SubgraphProvider ISubgraphPrices.GetProvider() => SubgraphProvider.ChainlinkPrice;

    private readonly IGraphQLClient _graphQLClient;
    private readonly string _baseToken = "/USD";

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

    public async Task<IEnumerable<Candle>> GetPrices(Ticker ticker, DateTime startDate, Timeframe timeframe)
    {
        var path = ticker.ToString() + _baseToken;
        var batchSize = 1000;
        var batchMax = 6;
        var priceRounds = new List<ChainlinkPrice>();
        var feedCondition = $@"{{ assetPair: ""{path}"" }}";

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

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

        var rounds = new List<Round>();

        // Format response
        foreach (var round in priceRounds)
        {
            var timestamp = int.Parse(round.Timestamp);
            rounds.Add(new Round
            {
                UnixTimestamp = timestamp,
                Value = (double.Parse(round.Price) / 1e8).ToString(),
                Date = DateHelpers.GetFromUnixTimestamp(timestamp)
            });
        }

        rounds.Sort((timeA, timeB) => timeA.UnixTimestamp - timeB.UnixTimestamp);

        return rounds.ToArray().GetCandles(timeframe, ticker);
    }

    public Task<decimal> GetVolume(Ticker ticker)
    {
        //var query = $"{{ assetPairs() {{ id }} }}";
        //var graphQuery = new GraphQLRequest
        //{
        //    Query = query
        //};

        //var response = await _graphQLClient.SendQueryAsync<AssetPairs>(graphQuery);
        throw new NotImplementedException();
    }

    public async Task<IEnumerable<Ticker>> GetTickers()
    {
        var batchSize = 100;
        var batchMax = 10;
        var tickers = new List<Ticker>();

        for (int i = 0; i < batchMax; i++)
        {
            var query = $"{{ assetPairs(first: {batchSize}, skip: {i * batchSize}) {{ id }} }}";
            var graphQuery = new GraphQLRequest
            {
                Query = query
            };

            var response = await _graphQLClient.SendQueryAsync<ChainlinkAssetPairs>(graphQuery);
            
            if (response.Data?.AssetPairs != null)
            {
                tickers.AddRange(ParseTickers(response.Data.AssetPairs));
            }
        }

        return tickers;
    }

    private List<Ticker> ParseTickers(List<AssetPair> pairs)
    {
        var tickers = new List<Ticker>();
        foreach (var pair in pairs)
        {
            var items = pair.Id.Split('/');

            if (items.Length == 2 && items[1] == Constants.Stablecoins.Usd)
            {
                try
                {
                    var ticker = MiscExtensions.ParseEnum<Ticker>(items[0]);
                    tickers.Add(ticker);
                }
                catch (Exception ex) { }
            }
        }

        return tickers;
    }
}
