using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Domain.Candles;
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm;
using Managing.Infrastructure.Evm.Abstractions;
using Managing.Infrastructure.Exchanges;
using Managing.Infrastructure.Exchanges.Abstractions;
using Managing.Infrastructure.Exchanges.Exchanges;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
using static Managing.Common.Enums;
using Ticker = Managing.Common.Enums.Ticker;

namespace Managing.Infrastructure.Tests
{
    public class ExchangeServicesTests
    {
        private readonly IExchangeService _exchangeService;
        public readonly string PublicAddress = "";

        public List<ISubgraphPrices> Subgraphs;

        public ExchangeServicesTests()
        {
            ILoggerFactory doesntDoMuch = new NullLoggerFactory();
            var candleRepository = new Mock<ICandleRepository>().Object;
            var cacheService = new Mock<ICacheService>().Object;
            var evmManager = new EvmManager(Subgraphs, EvmManagerTests.CreateWebProxyService(), cacheService);
            var evmProcessor = new EvmProcessor(new Mock<ILogger<EvmProcessor>>().Object, evmManager);
            var exchangeProcessors = new List<IExchangeProcessor>()
            {
                evmProcessor
            };

            _exchangeService = new ExchangeService(doesntDoMuch.CreateLogger<ExchangeService>(), candleRepository,
                exchangeProcessors);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.BTC)]
        public async void Should_Return_Price_For_Given_Ticker(TradingExchanges exchange, Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var price = await _exchangeService.GetPrice(account, ticker, DateTime.Now);
            Assert.IsType<decimal>(price);
            Assert.InRange<decimal>(price, 0, 1000000);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.ADA)]
        public async Task Should_Return_Candle_For_Given_Ticker(TradingExchanges exchange, Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var candle = await _exchangeService.GetCandle(account, ticker, DateTime.Now);
            Assert.IsType<Candle>(candle);
            Assert.InRange<decimal>(candle.High, 0, 1000000);
            Assert.InRange<decimal>(candle.Low, 0, 1000000);
            Assert.InRange<decimal>(candle.Open, 0, 1000000);
            Assert.InRange<decimal>(candle.Close, 0, 1000000);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm)]
        public void Should_Return_Balance(TradingExchanges exchange)
        {
            var account = PrivateKeys.GetAccount();
            var balance = _exchangeService.GetBalance(account).Result;
            Assert.IsType<decimal>(balance);
            Assert.True(balance >= 0);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, "0x2875673415c66bf05091eeff3887e0d40136d5ea443a4e63e7f4e41a6580575e",
            Ticker.BTC)]
        public void Should_Return_Trade_For_Given_OrderId(TradingExchanges exchange, string orderId,
            Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var trade = _exchangeService.GetTrade(account, orderId, ticker).Result;
            Assert.IsType<Trade>(trade);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.BTC)]
        public void Should_Return_List_Of_Candle_Given_Ticker(TradingExchanges exchange, Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var candles = _exchangeService
                .GetCandles(account, ticker, DateTime.Now.AddDays(-10), Timeframe.OneDay, true)
                .Result;
            Assert.IsType<List<Candle>>(candles);
            Assert.InRange(candles.Count, 1, 15);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.ADA, "7INRiu79cv2nCONNlILPu0")]
        public void Should_Return_Long_Trade(TradingExchanges exchange, Ticker ticker, string exchangeOrderId)
        {
            var account = PrivateKeys.GetAccount();
            var trade = _exchangeService.GetTrade(account, exchangeOrderId, ticker).Result;
            Assert.IsType<Trade>(trade);
            Assert.True(trade.Direction == TradeDirection.Long);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.ADA, "AQKzJpDNrfVjuq81baPLfR")]
        public void Should_Return_Short_Trade(TradingExchanges exchange, Ticker ticker, string exchangeOrderId)
        {
            var account = PrivateKeys.GetAccount();
            var trade = _exchangeService.GetTrade(account, exchangeOrderId, ticker).Result;
            Assert.IsType<Trade>(trade);
            Assert.True(trade.Direction == TradeDirection.Short);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.BTC)]
        public async void Should_Return_Balance_For_Ticker(TradingExchanges exchange, Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var balance = await _exchangeService.GetQuantityInPosition(account, ticker);
            Assert.IsType<decimal>(balance);
            Assert.True(balance > 0);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.ADA)]
        public void Should_Return_Trade_List_For_Ticker(TradingExchanges exchange, Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var trades = _exchangeService.GetTrades(account, ticker).Result;
            Assert.IsType<List<Trade>>(trades);
            Assert.True(trades.Count > 0);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm)]
        public void Should_Return_Fee(TradingExchanges exchange)
        {
            var account = PrivateKeys.GetAccount();
            var fee = _exchangeService.GetFee(account);
            Assert.IsType<decimal>(fee);
            Assert.True(fee > 0);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.BTC)]
        public void Should_Return_Volume(TradingExchanges exchange, Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var volume = _exchangeService.GetVolume(account, ticker);
            Assert.IsType<decimal>(volume);
            Assert.True(volume > 0);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.BTC)]
        public async void Should_Return_Open_Order(TradingExchanges exchange, Ticker ticker)
        {
            var account = PrivateKeys.GetAccount();
            var trades = await _exchangeService.GetOpenOrders(account, ticker);
            Assert.IsType<List<Trade>>(trades);
        }

        [Theory]
        [InlineData(TradingExchanges.Evm, Ticker.BTC, 0.1, TradeDirection.Long)]
        [InlineData(TradingExchanges.Evm, Ticker.BTC, 700, TradeDirection.Long)]
        [InlineData(TradingExchanges.Evm, Ticker.BTC, 700, TradeDirection.Short)]
        public async void Should_Return_Best_Price(
            TradingExchanges exchange,
            Ticker ticker,
            decimal quantity,
            TradeDirection direction)
        {
            var account = PrivateKeys.GetAccount();
            var lastPrice = await _exchangeService.GetPrice(account, ticker, DateTime.UtcNow);
            var bestPrice = await _exchangeService.GetBestPrice(account, ticker, lastPrice, quantity, direction);

            Assert.IsType<decimal>(bestPrice);

            var percentageDiff = ((bestPrice * 100) / lastPrice) - 100;
            Assert.True(Math.Abs(percentageDiff) < 1);
        }
    }
}