using CryptoExchange.Net.Authentication;
using FTX.Net.Interfaces.Clients;
using FTX.Net.Objects;
using Managing.Common;
using Managing.Domain.Accounts;
using Managing.Domain.Candles;
using Managing.Domain.Statistics;
using Managing.Domain.Trades;
using Managing.Infrastructure.Exchanges.Helpers;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using static Managing.Common.Enums;

namespace Managing.Infrastructure.Exchanges.Exchanges;

public class FtxProcessor : BaseProcessor
{
    private IFTXClient _ftxClient;
    private ILogger<FtxProcessor> _logger;

    public FtxProcessor(ILogger<FtxProcessor> logger)
    {
        _logger = logger;
    }

    public override Enums.TradingExchanges Exchange() => Enums.TradingExchanges.Ftx;

    public override void LoadClient(Account account)
    {
        var credentials = new ApiCredentials(account.Key, account.Secret);

        var ftxConfig = new FTXClientOptions()
        {
            SubaccountName = account.Name
        };
    }


    public override async Task<bool> CancelOrder(Account account, Ticker ticker)
    {
        LoadClient(account);
        var ftxResult = await _ftxClient.TradeApi.Trading.CancelAllOrdersAsync(FtxHelpers.ToFtxTicker(ticker));
        return ftxResult.Success;
    }

    public override async Task<decimal> GetBalance(Account account, bool isForPaperTrading = false)
    {
        LoadClient(account);

        var balance = 0m;
        var ftxBalance = await _ftxClient.TradeApi.Account.GetBalancesAsync();
        foreach (var item in ftxBalance.Data)
        {
            balance += item.UsdValue;
        }

        return balance;
    }

    public override Candle GetCandle(Account account, Ticker ticker, DateTime date)
    {
        LoadClient(account);

        var ftxKlines = _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker),
            FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data;
        if (ftxKlines != null && ftxKlines.Any())
        {
            var lastCandle = ftxKlines.ToList().LastOrDefault();
            return CandleHelpers.Map(lastCandle);
        }

        return null;
    }

    public override async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate,
        Timeframe timeframe)
    {
        LoadClient(account);

        var candles = new List<Candle>();
        var ftxCandles = await _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker),
            FtxHelpers.Map(timeframe), startDate);

        if (ftxCandles.Success)
            candles.AddRange(ftxCandles.Data.SkipLast(1)
                .Select(ftxKline => FtxHelpers.Map(ftxKline, ticker, account.Exchange, timeframe)));

        return candles;
    }

    public override decimal GetFee(Account account, bool isForPaperTrading = false)
    {
        LoadClient(account);
        var ftxResult = _ftxClient.TradeApi.Account.GetAccountInfoAsync().Result.Data;
        return ftxResult.TakerFee;
    }

    public override decimal GetPrice(Account account, Ticker ticker, DateTime date)
    {
        LoadClient(account);
        var ftxKlines = _ftxClient.TradeApi.ExchangeData.GetKlinesAsync(FtxHelpers.ToFtxTicker(ticker),
            FTX.Net.Enums.KlineInterval.OneMinute, date.AddHours(-2.5)).Result.Data;
        if (ftxKlines != null && ftxKlines.Any())
        {
            return ftxKlines.ToList().LastOrDefault().ClosePrice;
        }

        return 0;
    }

    public override async Task<decimal> GetQuantityInPosition(Account account, Ticker ticker)
    {
        var ftxTickerBalance = _ftxClient.TradeApi.Account.GetPositionsAsync().Result.Data;
        return ftxTickerBalance.FirstOrDefault(f => f.Future == FtxHelpers.ToFtxTicker(ticker)).Quantity;
    }

    public override async Task<Trade> GetTrade(Account account, string order, Ticker ticker)
    {
        LoadClient(account);
        var ftxOrder = await _ftxClient.TradeApi.Trading.GetOrderByClientOrderIdAsync(order);
        return FtxHelpers.Map(ftxOrder.Data);
    }

    public override async Task<List<Trade>> GetTrades(Account account, Ticker ticker)
    {
        LoadClient(account);
        var ftxOrder = await _ftxClient.TradeApi.Trading.GetOrdersAsync(ticker.ToString());
        return (List<Trade>)ftxOrder.Data.Select(o => FtxHelpers.Map(o));
    }

    public override decimal GetVolume(Account account, Ticker ticker)
    {
        var futureStats = _ftxClient.TradeApi.ExchangeData.GetFutureStatsAsync(FtxHelpers.ToFtxTicker(ticker)).Result
            .Data;
        return futureStats.Volume;
    }

    public override async Task<Trade> OpenTrade(Account account, Ticker ticker, TradeDirection direction, decimal price,
        decimal quantity, decimal? leverage = null, TradeType tradeType = TradeType.Limit, bool reduceOnly = false,
        bool isForPaperTrading = false, DateTime? currentDate = null, bool ioc = true,
        decimal? stopLossPrice = null,
        decimal? takeProfitPrice = null)
    {
        LoadClient(account);

        var trade = new Trade(DateTime.Now, direction, TradeStatus.PendingOpen, tradeType, ticker, quantity, price,
            leverage, "", "");

        trade.SetQuantity(quantity, 6);

        Trade ftxOrder;
        if (tradeType == TradeType.StopLoss || tradeType == TradeType.TakeProfitLimit ||
            tradeType == TradeType.StopMarket)
        {
            var ftxTriggerOrderType = FtxHelpers.FtxTriggerOrderTypeMap(tradeType);
            var ftxResult = await _ftxClient.TradeApi.Trading.PlaceTriggerOrderAsync(FtxHelpers.ToFtxTicker(ticker),
                direction != TradeDirection.Long ? FTX.Net.Enums.OrderSide.Sell : FTX.Net.Enums.OrderSide.Buy,
                ftxTriggerOrderType,
                triggerPrice: price,
                reduceOnly: true,
                retryUntilFilled: false,
                quantity: quantity);
            _logger.LogInformation("Exchange result : {0}", JsonConvert.SerializeObject(ftxResult));
            ftxOrder = FtxHelpers.Map(ftxResult, leverage);
        }
        else
        {
            var ftxOrderType = FtxHelpers.FtxOrderTypeMap(tradeType);
            var ftxResult = await _ftxClient.TradeApi.Trading.PlaceOrderAsync(FtxHelpers.ToFtxTicker(ticker),
                direction != TradeDirection.Long ? FTX.Net.Enums.OrderSide.Sell : FTX.Net.Enums.OrderSide.Buy,
                ftxOrderType,
                price: ftxOrderType == FTX.Net.Enums.OrderType.Limit ? price : null,
                quantity: quantity,
                clientOrderId: Guid.NewGuid().ToString(),
                immediateOrCancel: ioc);
            _logger.LogInformation("Exchange result : {0}", JsonConvert.SerializeObject(ftxResult));
            ftxOrder = FtxHelpers.Map(ftxResult, leverage);
        }

        trade.SetStatus(ftxOrder.Status);
        trade.SetExchangeOrderId(ftxOrder.ExchangeOrderId);
        trade.SetMessage(ftxOrder.Message);
        trade.Price = ftxOrder.Price;

        return trade;
    }

    public override Orderbook GetOrderbook(Account account, Ticker ticker)
    {
        LoadClient(account);
        var ftxOrderBook = _ftxClient.TradeApi.ExchangeData.GetOrderBookAsync(FtxHelpers.ToFtxTicker(ticker), 100)
            .Result;
        return FtxHelpers.Map(ftxOrderBook);
    }

    public override Task<List<Balance>> GetBalances(Account account, bool isForPaperTrading = false)
    {
        throw new NotImplementedException();
    }

    public override Task<List<Trade>> GetOrders(Account acount, Ticker ticker)
    {
        throw new NotImplementedException();
    }

    public override Task<Trade> GetTrade(string reference, string orderId, Ticker ticker)
    {
        throw new NotImplementedException();
    }

    public override Task<List<FundingRate>> GetFundingRates()
    {
        throw new NotImplementedException();
    }

    public override Task<IEnumerable<Position>> GetPositions(Account account)
    {
        throw new NotImplementedException();
    }
}