using Dapper;
using Elasticsearch.Net;
using iTextSharp.text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Nest;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using XYY.Common.Standard;
using XYY.Core.Standard.Data.Infrastructure;
using XYY.Core.Standard.Data.Redis;
using XYY.Service.Standard.First.ESB;
using XYY.TaskTrack.Standard;

namespace XYY.Service.Standard.First.ESB
{
    public interface IESBService
    {
        Task<List<ZZESBTrackingApiInfo>> ConvertESBTracking(List<TrackingDto> dtos, int apiCustomerId);
        void SendTask(List<ZZESBTrackingApiInfo> taskApiInfo);
        Task PerformTasks(List<ZZESBTrackingApiInfo> Tasks);
        Task Update(List<TrackingDto> dtos, int apiCustomerId);
        Task<PageResult<ZZESBTrackingApiInfo>> GetPageResult(QueryModel queryModel);
        Task Retry(string keys);
        Task TestTask(Dto dto);
        Task TestTask(Dto2 dtos);
        Task<List<Dto>> Reader(string trackingNumber);
    }

    public enum Node
    {
        CreateOrder = 1,
        InStock = 2,
        OutStock = 3,
        CustomsClearance = 4,
        Traffic = 5,
        Depart = 6,
        Arrival = 7,
        Clearance = 8,
        Online = 9,
        Transportation = 10,
        Delivery = 11
    }

    public class Dto2
    {
        public string TrackingNumber
        {
            get; set;
        }

        public List<Dto> Dtos { get; set; }
    }

    public class Dto
    {
        public string TimeZone { get; set; }
        public string DateTime { get; set; }
        public Node NodeType { get; set; }
        public string Node { get; set; }
        public string Location { get; set; }
        public bool IsLast { get; set; }
        public string AWB { get; set; }
        public string CountryCode { get; set; }
        public string TrackingNumber { get; set; }
        public string UID { get; set; }
    }


    public partial class ESBTrackingService : IESBService
    {
        IUnitOfWork _unitOfWork;
        IDatabase db;
        IServer server;
        IWebHook webHook;
        IMQManager mQManager;
        IDatabase db4;
        ILogger<IESBService> logger;
        public ESBTrackingService(IUnitOfWork unitOfWork, RedisHelper helper, IMQManager mQManager, ILogger<IESBService> logger)
        {
            _unitOfWork = unitOfWork;
            this.db = helper.GetDatabase();
            server = helper.GetServer();
            this.webHook = new QYWXWebHook("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5eb4675a-773a-40a0-9bad-745501122580");
            this.mQManager = mQManager;
            this.logger = logger;
            this.db4 = helper.GetDatabase(4);
        }

        public async Task Update(List<TrackingDto> dtos, int apiCustomerId)
        {
            var list = await ConvertESBTracking(dtos, apiCustomerId);
            await PerformTasks(list);
        }

        public async Task TestTask(Dto dto)
        {
            //db.SetAdd("OnTest#" + dto.TrackingNumber, dto.Node + "#" + dto.DateTime.ToString("MM-dd HH:mm:ss"));
        }

        public async Task TestTask(Dto2 dtos)
        {
            db.HashSet(dtos.TrackingNumber, dtos.Dtos.Select
                (x => new HashEntry(x.UID, Newtonsoft.Json.JsonConvert.SerializeObject(x))).ToArray());
        }

        public async Task<List<Dto>> Reader(string trackingNumber)
        {
            var h = db4.HashGetAll(trackingNumber);
            var r = h.Select(x => Newtonsoft.Json.JsonConvert.DeserializeObject<Dto>(x.Value)).ToList();
            r = r.OrderBy(x => x.DateTime).ToList();
            return (r);
        }




        public async Task<List<string>> GetTracking(string TrackingNumber)
        {
            return db.SortedSetRangeByRank("OnTest#" + TrackingNumber).Select(x => x.ToString()).ToList();
        }


        public async Task Retry(string keys)
        {
            ThreadPool.SetMinThreads(200, 200);
            string sql = @"select [trackNum]
      ,[location]
      ,[state]
      ,convert(nvarchar, [time],120) as time
      ,[timeAsUnix]
      ,[country]
      ,[key]
      ,[PushStatus]
      ,[ResultMsg]
      ,[CreateTime] from ZZESBTrackingApiInfo(nolock) where [key] in @keys";
            var list = (await _unitOfWork.QueryBySqlAsync<ZZESBTrackingApiInfo>(sql, null, new
            {
                keys = keys.Split(',')
            })).ToList();

            var batch2 = db.CreateBatch();
            //如果存储出错的话，我们清理掉相关单号
            foreach (var item in list)
            {
                db.KeyDeleteAsync(item.key);
            }
            batch2.Execute();

            await _unitOfWork.ExecuteAsync("delete ZZESBTrackingApiInfo where [key] in @keys", new
            {
                keys = keys.Split(',')
            });

            await PerformTasks(list);
        }

        public async Task<List<ZZESBTrackingApiInfo>> ConvertESBTracking(List<TrackingDto> dtos, int apiCustomerID)
        {
            if (dtos.Count > 100)
                throw new Exception("每次仅允许更新100条记录");

            logger.LogInformation(dtos[0].Logs.Count.ToString());

            dtos.ForEach(x =>
            {
                var signTime = x.Logs.FirstOrDefault(x => x.Status == 7);
                if (signTime != null)
                {
                    //排除到轨迹发生时间和签收时间一致的轨迹，防止签收轨迹被覆盖。
                    x.Logs = x.Logs.Where(x => x.TriggerTime != signTime.TriggerTime
                    || x.Status == 7).ToList();
                }

            });

            var mappings = ESBMappingService.GetZZESBMapping();
            string sql = $@"select a.TrackingNumber,a.TransferNumber,a.ReceiverCountryCode,b.DownloadTime,a.CustomerId 
                            from Order_Order(nolock)a left join  Order_ZZESBJJData(nolock)b on a.TransferNumber =b.TransferNumber
                            where   ServiceOrderNumber4 = 'HNESB'  
                            and (a.TrackingNumber in @numbers )
union
select a.TrackingNumber,a.TransferNumber,a.ReceiverCountryCode,b.DownloadTime ,a.CustomerId
                            from Order_Order(nolock)a left join  Order_ZZESBJJData(nolock)b on a.TransferNumber =b.TransferNumber
                            where ServiceOrderNumber4 = 'HNESB'  
                            and (a.TransferNumber in @numbers ) ";
            using (SqlConnection connection = new SqlConnection(_unitOfWork.ConnectionAddress))
            {
                //数据交互我们使用单链接
                using (var uof = new UnitOfWork(connection, _unitOfWork.ConnectionAddress))
                {
                    var orders = await uof.QueryBySqlAsync<dynamic>(sql, null, new { numbers = dtos.Select(x => x.TrackingNumber).ToList() });
                    if (apiCustomerID != 1 && orders.Any(x => x.CustomerId != apiCustomerID))
                    {
                        throw new Exception("订单无权限操作");
                    }
                    var trackingInfos = new ConcurrentBag<ZZESBTrackingApiInfo>();
                    mappings.ForEach(x => { x.Key = x.Key.Replace("\u00A0+", " "); });
                    //去重
                    mappings = mappings.Where(x => x.Event != null).GroupBy(x => new ZZESBMapping
                    {
                        Content = x.Content,
                        CountryCode = x.CountryCode,
                        Event = x.Event,
                        Key = x.Key
                    }).Select(x => x.Key).ToList();
                    ConcurrentBag<TrackingDto> bags = new ConcurrentBag<TrackingDto>(dtos);
                    //ConcurrentBag<ZZESBMapping> bagMappings = new ConcurrentBag<ZZESBMapping>(mappings);

                    Parallel.ForEach(bags, item =>
                    {
                        var o = orders.FirstOrDefault(x => x.TrackingNumber == item.TrackingNumber || x.TransferNumber == item.TrackingNumber);
                        var bagLogs = new ConcurrentBag<TrackingLogDto>(item.Logs);
                        Parallel.ForEach(bagLogs, log =>
                        {
                            ConcurrentBag<ZZESBMapping> bagMappings = new ConcurrentBag<ZZESBMapping>(mappings.Where(x => x.CountryCode == o.ReceiverCountryCode));
                            Parallel.ForEach(bagMappings, mapping =>
                            {
                                if (log.StatusString.Contains(mapping.Key, StringComparison.OrdinalIgnoreCase))
                                {
                                    if (o.DownloadTime == null || log.TriggerTime < o.DownloadTime.AddHours(1))
                                    {
                                    }
                                    else
                                    {
                                        string CountryCode = o.ReceiverCountryCode;
                                        if (log.StatusString == "During domestic transportation")
                                        {
                                            CountryCode = "CN";
                                        }
                                        if (log.StatusString == "it is under customs clearance.")
                                        {
                                            CountryCode = "CN";
                                        }
                                        if (log.StatusString == "Item is ready for departure.")
                                        {
                                            CountryCode = "CN";
                                            log.TriggerTime = log.TriggerTime.AddMinutes(10);
                                        }
                                        if (log.StatusString == "Item is being presented to customs.")
                                        {
                                            log.TriggerTime = log.TriggerTime.AddMinutes(20);
                                        }
                                        if (log.StatusString == "Item was released from customs.")
                                        {
                                            log.TriggerTime = log.TriggerTime.AddMinutes(30);
                                        }
                                        string l = mapping.Content.Replace("轨迹", log.StatusString);
                                        l = l.Replace("位置", log.Location);
                                        string l2 = string.Join(string.Empty, l.Split('+'));

                                        var esbtrack = new ZZESBTrackingApiInfo
                                        {
                                            time = log.TriggerTime.ToString_yyyyMMddHHmmss(),
                                            country = CountryCode,
                                            CreateTime = DateTime.Now,
                                            timeAsUnix = CurrentTimeStamp(log.TriggerTime),
                                            trackNum = o.TransferNumber,
                                            location = l2,
                                            state = mapping.Event,
                                            PushStatus = PushStatus.未推送
                                        };
                                        esbtrack.key = esbtrack.trackNum + esbtrack.timeAsUnix;
                                        trackingInfos.Add(esbtrack);
                                    }
                                }
                            });
                        });
                    });
                    return trackingInfos.ToList();
                }
            }
        }
        public static long CurrentTimeStamp(DateTime time)
        {
            var ts = time - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            long times = Convert.ToInt64(ts.TotalSeconds);
            return times;
        }

        public async Task PerformTasks(List<ZZESBTrackingApiInfo> taskApiInfo)
        {
            ThreadPool.SetMinThreads(200, 200);
            logger.LogInformation(taskApiInfo.Count.ToString());
            List<ZZESBTrackingApiInfo> esb2 = new List<ZZESBTrackingApiInfo>();
            foreach (var task in taskApiInfo)
            {
                if (db.KeyExists(task.key))
                    continue;
                else
                {
                    esb2.Add(task);
                }
            }
            if (esb2.Count > 0)
            {
                for (int i = 0; i < Math.Ceiling(esb2.Count / 100.0m); i++)
                {
                    var esb3 = esb2.Skip(i * 100).Take(100).ToList();
                    SendESBApi(esb3);
                    var batch = db.CreateBatch();
                    foreach (var item in esb3)
                    {
                        //缓存60天
                        db.StringSetAsync(item.key, "1", new TimeSpan(60, 0, 0, 0));
                    }
                    batch.Execute();
                    //直接存数据库
                    try
                    {
                        //数据交互我们使用单链接
                        using (SqlConnection connection = new SqlConnection(_unitOfWork.ConnectionAddress))
                        {
                            connection.Open();
                            using (var uof = new UnitOfWork(connection, _unitOfWork.ConnectionAddress))
                            {
                                await uof.BulkToDBAsync(esb3);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string exMsg = ex.Message;
                        if (!exMsg.Contains("PRIMARY KEY"))
                        {
                            var batch2 = db.CreateBatch();
                            //如果存储出错的话，我们清理掉相关单号
                            foreach (var item in esb2)
                            {
                                db.KeyDeleteAsync(item.key);
                            }
                            batch2.Execute();
                        }
                    }
                }


            }
            //db.ListLeftPush(waitDbKey,)
        }


        public string timeKey = "LastSendTime";

        public List<ZZESBTrackingApiInfo> SendESBApi(List<ZZESBTrackingApiInfo> taskApiInfo)
        {
            int i = 1;
            taskApiInfo.ForEach(x =>
            {
                x.SeqNum = i++;
                x.PushStatus = PushStatus.已推送;
                x.ResultMsg = string.Empty;
            });
            List<ZZESBTrackingApiInfo> list = new List<ZZESBTrackingApiInfo>();
            string url = @"http://47.102.46.210:9002/hnesb/preset/api/addPresetTrack";
            ESBReqRoot root = new ESBReqRoot();
            root.username = "qdx";
            root.sign = "66EE08A6B3DA4E0680D228FD088E6ED3";
            root.content = taskApiInfo.Select(x => new ESBReqContentItem
            {
                country = x.country,
                location = x.location,
                seqNum = x.SeqNum,
                state = x.state,
                time = x.time,
                trackNum = x.trackNum
            }).ToList();
            RestSharp.RestClient client = new RestSharp.RestClient(url);
            RestSharp.RestRequest restRequest = new RestSharp.RestRequest(RestSharp.Method.POST);
            restRequest.AddJsonBody(root);
            var restResponse = client.Execute(restRequest);
            if (restResponse.StatusCode != System.Net.HttpStatusCode.OK)
            {
                webHook.SendMarkdown("河南邮政ESB Api_Http请求异常", restResponse.ErrorMessage,
                    restResponse.ErrorMessage, 2 * 60);
            }
            else
            {
                ESBResult ESBResult = Newtonsoft.Json.JsonConvert.DeserializeObject<ESBResult>(restResponse.Content);
                if (ESBResult.code != "P01")
                {
                    if (ESBResult.content != null)
                    {
                        var fitter = ESBResult.content.Where(x => x.state != "AO1" && x.state != "A02");
                        if (fitter.Count() > 0)
                        {
                            webHook.SendMarkdown("河南邮政ESB Api 返回异常", string.Join("\n", fitter.Select(x => x.msg)), "河南邮政ESB Api 返回明细异常", 2 * 60);
                        }
                        //失败的
                        foreach (var item in fitter)
                        {
                            var t = taskApiInfo.FirstOrDefault(x => x.SeqNum == item.seqNum);
                            if (t != null)
                            {
                                t.ResultMsg = item.msg;
                                t.PushStatus = PushStatus.推送失败;
                            }
                        }

                    }
                    else
                    {
                        //初期单量少间隔2分钟提醒一次即可
                        webHook.SendMarkdown("河南邮政ESB Api 结果异常", ESBResult.msg, "河南邮政ESB Api 结果异常", 2 * 60);
                        foreach (var item in taskApiInfo)
                        {
                            item.ResultMsg = ESBResult.msg;
                            item.PushStatus = PushStatus.推送失败;
                        }
                    }
                }
            }
            return taskApiInfo;
        }

        public async void SendTask(List<ZZESBTrackingApiInfo> taskApiInfo)
        {
            await mQManager.Publish(taskApiInfo);
        }

        public async Task<PageResult<ZZESBTrackingApiInfo>> GetPageResult(QueryModel queryModel)
        {
            return await _unitOfWork.GetPagingList2Async<ZZESBTrackingApiInfo>(queryModel);
        }
    }
}
