using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using WebSocket.Interfaces;
using System.Linq;

namespace WebSocket.Clients
{
    public class CexClient : IWebClient
    {
        #region Members
        private IWebSocketForClient server;
        private Dictionary<string, Action<Command, string>> CommandAnswers = new Dictionary<string, Action<Command, string>>();
        //private Dictionary<string, object> data = new Dictionary<string, object>();
        //private string[] pairs;
        #endregion

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="server"></param>
        public CexClient(IWebSocketForClient server)
        {
            this.server = server;
            Register();
        }
        
        public void Connected()
        {
            //SendAuth();
            //wait connected message?
        }
        
        public void StartRequest(string[] pairs) {
            //data.Clear();
            //See https://cex.io/websocket-api#minute-data for info
            //this.pairs = pairs;
            Send(new { e = "init-ohlcv", i = "1m", rooms = new string[] { $"pair-{pairs[0]}" } });
            //SendAuth();
        }

        private void Update(string name, Command command, string message, Type dataType) {
            var data = new Dictionary<string, object>();
            data[name] = (Summary)JsonConvert.DeserializeObject(message, dataType, GetSettings());
            //this.server.updateData.Invoke(server.ExchangeId, "Cex", GetMarketOHLCVData(data));
            this.server.DataChanged("Cex", GetMarketOHLCVData(data));
        }

        private void Register()
        {
            On("connected", (c, obj) => SendAuth());
            //On("auth", (c, obj) =>  SendRequest());
            On("ping", (c, obj) => SendPong());
            //On("ticker", (c, obj) => Update(c.E, c, obj, typeof(Ticker)));
            ////On("order-book-subscribe", (c, obj) => Update(c.E + " " + c.Oid, c, obj, typeof(OrderBookSubscribe)));
            //On("order-book-subscribe", (c, obj) => SaveSubscribe(c, obj));
            //On("md_update", (c, obj) => UpdateSubscribe(c, obj));
            On("ohlcv1m", (c, obj) => Update(c.E, c, obj, typeof(Summary)));
        }

        //private void SendRequest()
        //{
        //    Send(new { e = "init-ohlcv", i = "1m", rooms = new string[] { $"pair-{this.pairs[0]}" } });
        //}

        public void Message(string message)
        {
            var parse = JsonConvert.DeserializeObject<Command>(message, GetSettings());
            if (parse.Oid != null && CommandAnswers.ContainsKey(parse.E + parse.Oid))
            {
                CommandAnswers[parse.E + parse.Oid](parse, message);
            }
            if (CommandAnswers.ContainsKey(parse.E))
            {
                CommandAnswers[parse.E](parse, message);
            }
            /*switch (parse.E)
            {
                case "connected":
                    SendAuth();
                    break;
                case "auth":
                    if (parse.Ok != null)
                    {
                        SetSubscribes();
                    }
                    break;
                case "ping":
                    SendPong();
                    break;
            }
            {
              "e": "init-ohlcv-data",
              "error": "Invalid pair",
              "data": [],
              "pair": "BTC:ETH"
            }
             */
        }

        public void Tick()
        {
            //'ticker' or 'get-balance'
        }

        public void Data(byte[] data)
        {
        }

        public void Data(Dictionary<string, object> data)
        {
        }

        public void Closed(bool byUser, string reason)
        {

        }

        public void Error(Exception ex)
        {
        }

        public void Error(string reason)
        {
        }

        public void SendFailed(string data, Exception ex)
        {
        }

        private void SendAuth()
        {
            var auth = new Auth() { Key = "RIxLksQs8PEhYUY9a6QTivB4k" };
            auth.CreateSignature("vpjTi5iCm4vafJOVxon9eMGaU");
            //Send(JsonConvert.SerializeObject(new AuthRequest() { Auth = auth }));
            Send(new AuthRequest() { Auth = auth });
        }

        private void Send(object obj)
        {
            server.Send(JsonConvert.SerializeObject(obj, GetSettings()));
        }

        private void SendPong()
        {
            Send(new Command() { E = "pong" });
        }

        private void On(string name, Action<Command, string> command)
        {
            CommandAnswers[name] = command;
        }

        private void Off(string name)
        {
            if (CommandAnswers.ContainsKey(name))
            {
                CommandAnswers.Remove(name);
            }
        }

        public Dictionary<string, IMarketOHLCVData> GetMarketOHLCVData(Dictionary<string, object> data)
        {
            return data.Select(x =>
            {
                var item = x.Value as Summary;
                return new MarketOHLCVData()
                {
                    PairSymbol = item.data.Pair,
                    HighPrice = item.data.HighPrice,
                    LowPrice = item.data.LowPrice,
                    OpenPrice = item.data.OpenPrice,
                    ClosePrice = item.data.ClosePrice,
                    Volume = item.data.Volume,
                    TimeStamp = item.data.TimeStamp,
                } as IMarketOHLCVData;
            }).ToDictionary(x => x.PairSymbol);
        }

        private JsonSerializerSettings GetSettings()
        {
            var settings = new JsonSerializerSettings()
            {
                ContractResolver = new LowerCaseUnderscoreContractResolver()
                //Formatting = Formatting.Indented
            };
            settings.Converters.Add(new Newtonsoft.Json.Converters.UnixDateTimeConverter());
            return settings;
        }
    }
}
