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

namespace WebSocket.Clients
{
    public class OKExClient : IWebClient
    {
        #region Members
        internal class OKExResponse
        {
            [JsonProperty(PropertyName = "channel")]
            public string Channel { get; set; }
            public int Binary { get; set; }
            public string Pair {
                get {
                    var arr = this.Channel.Split('_');
                    if (arr.Length > 4)
                    {
                        return arr[3].ToUpper() + arr[4].ToUpper();
                    }
                    return string.Empty;
                }

                set { } }
        }
        
        internal class OKExResponse2: OKExResponse
        {
            [JsonProperty(PropertyName = "data")]
            public JValue[][] Data { get; set; }
        }

        private IWebSocketForClient server;
        private Dictionary<string, Action<Command, string>> CommandAnswers = new Dictionary<string, Action<Command, string>>();
        #endregion

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="server"></param>
        public OKExClient(IWebSocketForClient server)
        {
            this.server = server;
            Register();
        }
        
        public void Connected()
        {
            //SendAuth();
            //wait connected message?
        }

        public void StartRequest(string[] pairs) {
            //Send(new [] { new { Event = "addChannel", Channel = $"ok_sub_spot_{pairs[0].ToLower()}_kline_1min" }});
            server.Send($"[{{'event':'addChannel','channel':'ok_sub_spot_{pairs[0].ToLower()}_kline_1min'}}]");
        }

        private void Register()
        {
            On("connected", (c, obj) => Connected());
            On("ping", (c, obj) => SendPong());
            //On("ohlcv1m", (c, obj) => Update(c.E, c, obj, typeof(Summary)));
        }

        public void Message(string message)
        {
            var parse = JsonConvert.DeserializeObject<IEnumerable<OKExResponse>>(message, GetSettings());
            var data = parse.FirstOrDefault();
            if (data != null)
            {
                if (data.Channel.StartsWith("ok_sub_spot"))
                {
                    dynamic jsonDe = JsonConvert.DeserializeObject(message, GetSettings());
                    if (jsonDe is JArray && jsonDe.Count > 0)
                    {
                        var rawData = jsonDe[0];
                        if (rawData != null && rawData.data is JArray)
                        {
                            var parse2 = JsonConvert.DeserializeObject<IEnumerable<OKExResponse2>>(message, GetSettings());
                            var data2 = parse2.FirstOrDefault();
                            if (data2 != null)
                            {
                                this.server.DataChanged("OKEx", GetMarketOHLCVData(new Dictionary<string, object> { { data2.Pair, data2 } }));
                            }
                        }
                    }
                }
            }
        }

        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 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 OKExResponse2;
                var itemData = item.Data[0];
                if (itemData != null)
                {
                    //[time, open price, highest price, lowest price, close price, volume]
                    //[string, string, string, string, string, string]
                    return new MarketOHLCVData()
                    {
                        TimeStamp = ConvertFromLong(itemData[0].ToObject<long>()),
                        OpenPrice = itemData[1].ToObject<decimal>(),
                        HighPrice = itemData[2].ToObject<decimal>(),
                        LowPrice = itemData[3].ToObject<decimal>(),
                        ClosePrice = itemData[4].ToObject<decimal>(),
                        Volume = itemData[5].ToObject<decimal>(),
                        PairSymbol = x.Key
                    } as IMarketOHLCVData;
                }
                else {
                    return null;
                }
            }).ToDictionary(x => x.PairSymbol);
        }

        #region Helpers
        private DateTime _1970Time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        private long ConvertToLong(DateTime time)
        {
            return (long)(time - _1970Time).TotalMilliseconds;
        }
        private DateTime ConvertFromLong(long ms)
        {
            return _1970Time.AddMilliseconds(ms);
        }

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

        //private static bool IsPropertyExist(dynamic settings, string name)
        //{
        //    if (settings is ExpandoObject)
        //        return ((IDictionary<string, object>)settings).ContainsKey(name);

        //    return settings.GetType().GetProperty(name) != null;
        //}
        #endregion
    }
}
