using Binance.Net.Clients;
using Binance.Net.Objects;
using Binance.TradeRobot.Model.Base;
using CryptoExchange.Net.Authentication;
using Newtonsoft.Json;
using SDKAdapter.Model;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace SDKAdapter.WebSockets.Order.Spot
{
    public class BinanceSpotOrderWebSocketClient : SpotOrderWebSocketClient
    {
        private BinanceSocketClient binanceSocketClient;
        private BinanceClient binanceClient;
        private CancellationTokenSource cancellationTokenSource;
        private string listenKey;
        private IList<Binance.Net.Enums.OrderStatus> ignoreOrderStateList;
        private System.Threading.Timer timer;
        private readonly long extendListenKeyPeriod = 1000 * 60 * 30;  //30分钟
        private string isolateMarginSymbol = ""; //逐仓杠杆专用

        public BinanceSpotOrderWebSocketClient(Enums.BusinessType businessType,
                                               long accountId,
                                               string apiKey,
                                               string secret,
                                               NLog.ILogger logger,
                                               Action<SpotOrderPublishInfo> onOrderUpdated) : base(businessType,
                                                                                                       accountId,
                                                                                                       apiKey,
                                                                                                       secret,
                                                                                                       logger,
                                                                                                       onOrderUpdated)
        {
            var spotClientOption = new BinanceApiClientOptions()
            {
                BaseAddress = "https://api.binance.com",
                ApiCredentials = new ApiCredentials(apiKey, secret)
            };
            //var usdFuturesClientOption = new BinanceApiClientOptions()
            //{
            //    BaseAddress = "https://fapi.binance.com",
            //    ApiCredentials = new ApiCredentials(apiKey, secret)
            //};
            binanceClient = new BinanceClient(new BinanceClientOptions()
            {
                //UsdFuturesApiOptions = usdFuturesClientOption,
                SpotApiOptions = spotClientOption
            });
            binanceSocketClient = new BinanceSocketClient();
            listenKey = string.Empty;
            ignoreOrderStateList = new List<Binance.Net.Enums.OrderStatus>()
            {
                Binance.Net.Enums.OrderStatus.New,
                Binance.Net.Enums.OrderStatus.PendingCancel,
                //Binance.Net.Enums.OrderStatus.PartiallyFilled,  //由于无法确定手续费时候存在局部,暂时忽略局部成交
                Binance.Net.Enums.OrderStatus.Insurance,
                Binance.Net.Enums.OrderStatus.Adl
            };
            timer = new Timer(new TimerCallback(ExtendListenKey), null, -1, extendListenKeyPeriod);

        }

        public override void Start(string symbol = "")
        {
            if (IsConnected)
                return;
            IsConnected = true;
            isolateMarginSymbol = symbol;
            cancellationTokenSource = new CancellationTokenSource();
            var getListenKeyResponse = binanceClient.SpotApi.Account.StartIsolatedMarginUserStreamAsync(isolateMarginSymbol).Result;
            if (!getListenKeyResponse.Success)
                throw new Exception(getListenKeyResponse.Error?.Message ?? "");
            listenKey = getListenKeyResponse.Data;
            _ = binanceSocketClient.SpotStreams.SubscribeToUserDataUpdatesAsync(listenKey,
                (e) =>
                {
                    try
                    {
                        var originData = new StringBuilder();
                        var isStop = false;
                        if (ignoreOrderStateList.Contains(e.Data.Status))
                        {
                            originData.Append($"不支持的订单状态{e.Data.Status}");
                            isStop = true;
                        }

                        else if (string.IsNullOrEmpty(e.Data.ClientOrderId))
                        {
                            originData.Append($"缺少ClientOrderId");
                            isStop = true;
                        }

                        long? robotId = null;
                        Enums.TradePolicy? tradePolicy = null;

                        if (!isStop)
                        {
                            var match = Regex.Match(e.Data.ClientOrderId, @"^([a-z0-9]{5})_(\d{15,})_(\d{1,2})$");
                            if (!match.Success)
                            {
                                originData.Append($"非机器人交易的ClientOrderId");
                                isStop = true;
                            }
                            else
                            {
                                robotId = long.Parse(match.Groups[2].Value);
                                tradePolicy = (Enums.TradePolicy)int.Parse(match.Groups[3].Value);
                            }
                        }
                        originData.Append($" {JsonConvert.SerializeObject(e.Data)}");
                        logger.Info(originData);
                        if (isStop)
                            return;

                        var orderState = Enums.SpotOrderState.Unknow;
                        switch (e.Data.Status)
                        {
                            case Binance.Net.Enums.OrderStatus.PartiallyFilled:
                            case Binance.Net.Enums.OrderStatus.Filled:
                            case Binance.Net.Enums.OrderStatus.Canceled:
                                orderState = (Enums.SpotOrderState)(int)e.Data.Status;
                                break;
                            case Binance.Net.Enums.OrderStatus.Rejected:
                                orderState = Enums.SpotOrderState.Rejected;
                                break;
                            case Binance.Net.Enums.OrderStatus.Expired:
                                orderState = Enums.SpotOrderState.Expired;
                                break;
                        }

                        OnOrderUpdated?.Invoke(new SpotOrderPublishInfo()
                        {
                            OrderId = e.Data.Id,
                            RobotId = robotId.Value,
                            TradePolicy = tradePolicy.Value,
                            Symbol = e.Data.Symbol,
                            AccountId = this.AccountId,
                            OrderType = (Enums.OrderType)(int)e.Data.Type,
                            SpotOrderState = orderState,
                            TradeDirection = (Enums.TradeDirection)(int)e.Data.Side,
                            ClientOrderId = e.Data.ClientOrderId,
                            CummulativeTradeAmount = e.Data.QuoteQuantityFilled,
                            CummulativeTradeQuantity = e.Data.QuantityFilled,
                            Exchange = Enums.Exchange.Binance,
                            Fee = e.Data.Fee,
                            FeeUnit = e.Data.FeeAsset,
                            LastTradeAmount = e.Data.LastQuoteQuantity,
                            LastTradePrice = e.Data.LastPriceFilled,
                            LastTradeQuantity = e.Data.LastQuantityFilled,
                            LastTradeTime = e.Data.UpdateTime,
                            CreateTime = e.Data.CreateTime,
                            LoggerName = logger.Name,
                            RejectedReason = e.Data.RejectReason.ToString()
                        });
                    }
                    catch (Exception ex)
                    {
                        logger.Error(ex);
                    }
                },
                (e) =>
                {

                },
                (e) =>
                {

                },
                (e) =>
                {

                },
                cancellationTokenSource.Token);
            try
            {
                timer.Change(extendListenKeyPeriod, extendListenKeyPeriod);
            }
            catch { }
        }

        public override void Stop(string symbol = "")
        {
            if (!IsConnected)
                return;
            IsConnected = false;
            cancellationTokenSource.Cancel();
            binanceSocketClient.SpotStreams.Dispose();
            cancellationTokenSource = null;
            _ = binanceClient.SpotApi.Account.CloseIsolatedMarginUserStreamAsync(symbol, listenKey).Result;
            listenKey = string.Empty;
            try
            {
                timer.Change(-1, extendListenKeyPeriod);
            }
            catch { }
        }

        private void ExtendListenKey(object? o)
        {
            if (string.IsNullOrEmpty(listenKey))
                return;
            if (BusinessType == Enums.BusinessType.IsolateMargin)
                _ = binanceClient.SpotApi.Account.KeepAliveIsolatedMarginUserStreamAsync(isolateMarginSymbol, listenKey);
        }
    }
}