using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XYY.Model.Standard.Channel;
using XYY.Model.Standard.Order;
using XYY.Service.Standard.ChannelApi.Base;
using RestSharp;
using static XYY.Service.Standard.ChannelApi.HangLe.Api;
using System.Net;
using System.Security.Cryptography;
using XYY.Common.Standard;
using System.Security.Policy;
using System.Threading.Channels;
using System.Threading;
using XYY.Service.Standard.ChannelApi.AddressHelper;

namespace XYY.Service.Standard.ChannelApi.PYD
{
    public class Api : IBaseApi
    {
        public string ServiceName => "PYD";

        private string CreateSign(string accessKey, string accessSecret, long timestamp, out string rawString)
        {
            string raw = $"{accessKey}{accessSecret}{timestamp}";
            rawString = raw;
            SHA1 sha1 = SHA1.Create();
            byte[] bytes = Encoding.UTF8.GetBytes(raw);
            byte[] cipherData = sha1.ComputeHash(bytes);
            return string.Join("", cipherData.Select(b => string.Format("{0:x2}", b)).ToArray());
        }


        public Task<List<BagDeclareErrosResult>> BagDeclare(BagDeclareRequest bagDeclareRequest)
        {
            throw new NotImplementedException();
        }

        public void CannelOrders(List<string> systemNO)
        {
            throw new NotImplementedException();
        }

        public void CannelOrdersByModel(logistics_ChannelCancel logistics_ChannelCancel)
        {
            throw new NotImplementedException();
        }

        public Task<List<string>> Confims(List<OrderConfimRequest> order, Logistics_Channel channel)
        {
            throw new NotImplementedException();
        }

        public async Task<UploadResult> DataUpload(Order_Order order, List<Order_OrderGoods> goods, Logistics_Channel channel, ExtendArg arg)
        {
            new USAddressCheck().CheckUSOrder(order, false);

            string url = "/order/submit";
            string refOrderNumber = order.SystemNo;

            CreateOrderParam param = GenCreateOrderParam(refOrderNumber, order, goods, arg);
            var result = await Send<CreateOrderParam, CreateOrderReponse>(param, channel, url);


            if (result.IsSuccess)
            {
                LabelResponse labelDataResp = await RequestLabel(channel, refOrderNumber);
                if (labelDataResp.code != "200")
                {
                    string msg = $"获取面单失败:{labelDataResp.msg}";
                    throw new Exception(msg);
                }

                LabelData labelData = labelDataResp.data;
                var uploadResult = new UploadResult
                {
                    DownloadUrl = labelData.labelUrl,
                    IsSuccess = true,
                    MPSTrackings = new Dictionary<string, string>(),
                    TrackingNumber = labelData.trackNumber
                };
                for (int i = 0; i < labelData.packageDetailList.Count; i++)
                {
                    uploadResult.MPSTrackings.Add(arg.Order.Boxs[i].BoxServiceNumber, labelData.packageDetailList[i].childTrackNumber);
                }
                return uploadResult;
            }
            else
            {
                throw new Exception("下单失败" + result.msg);
            }
        }

        private CreateOrderParam GenCreateOrderParam(string userOrderNumber, Order_Order order, List<Order_OrderGoods> goods, ExtendArg arg)
        {
            CreateOrderParam param = new CreateOrderParam
            {
                userOrderNumber = userOrderNumber,
                fbdNumber = arg.Order.FbaOrderNumber,
                shipTo = CreateShipTo(order),
                packageDetailList = CreatePackDetial(order, goods, arg)
            };
            return param;
        }

        private List<PackageDetailList> CreatePackDetial(Order_Order order, List<Order_OrderGoods> goods, ExtendArg arg)
        {
            List<PackageDetailList> result = new List<PackageDetailList>();
            result.AddRange(arg.Order.Boxs.Select(b => new PackageDetailList
            {
                enName = "1",
                height = b.OpHeight,
                length = b.OpLength,
                number = b.BoxNumber,
                weight = b.OpInputWeight,
                width = b.OpWidth,
                zhName = "1",
                remark = b.BoxNumber,
            }));
            return result;
        }

        private ShipTo CreateShipTo(Order_Order order)
        {
            var addressArr = order.ReceiverStreet.Replace("|", "").ToArray(30);
            var result = new ShipTo
            {
                addressLine = addressArr[0],
                addressLine2 = addressArr.Length > 1 ? addressArr[1] : string.Empty,
                city = order.ReceiverCity,
                companyName = string.IsNullOrWhiteSpace(order.ReceiverCompany) ? order.ReceiverName : order.ReceiverCompany,
                countryCode = order.ReceiverCountryCode,
                name = order.ReceiverName,
                phone = order.ReceiverPhone,
                postalCode = order.ReceiverZipCode,
                stateProvinceCode = order.ReceiverState,
            };
            return result;
        }

        private async Task<LabelResponse> RequestLabel(Logistics_Channel channel, string refOrderNumber)
        {
            int maxCount = 12;
            int interval = 10 * 1000;
            for (var count = 0; count <= maxCount; count++)
            {
                LabelResponse label = await RequestLabelCore(channel, refOrderNumber);

                if (label.msg?.Contains("暂无面单") == true)
                {
                    Thread.Sleep(interval);
                }
                else
                {
                    return label;
                }
            }

            return new LabelResponse { code = "0", msg = $"已重试:{maxCount}次仍不能获取面单" };
        }

        private async Task<LabelResponse> RequestLabelCore(Logistics_Channel channel, string refOrderNumber)
        {
            string url = "/order/label";
            GetLabelParam getLabelParam = new GetLabelParam { userOrderNumber = refOrderNumber };
            var result = await Send<GetLabelParam, LabelResponse>(getLabelParam, channel, url);
            return result;
        }

        public async Task<T2> Send<T1, T2>(T1 data, Logistics_Channel channel, string action)
        {
            RestSharp.RestClient client = new RestSharp.RestClient(channel.ApiHost);
            RestSharp.RestRequest request = new RestSharp.RestRequest(action, RestSharp.Method.POST, RestSharp.DataFormat.Json);
            var signTuple = SetupAuthHeader(request, channel);

            request.AddJsonBody(data);
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
            var sendTime = DateTime.Now;
            var resposne = client.Execute(request);
            if (resposne.StatusCode == System.Net.HttpStatusCode.OK)
            {
                try
                {
                    return Newtonsoft.Json.JsonConvert.DeserializeObject<T2>(resposne.Content);
                }
                catch (Exception ex)
                {
                    throw new Exception("Json返回结果解析失败" + ex.Message);
                }
            }
            else
            {
                string msg = resposne.Content ?? resposne.ErrorMessage;
                if (msg != null && msg.Contains("非法签名"))
                {
                    msg = $"非法签名:请求时间:{sendTime.ToString_HHmmss()},TimeStamp:{signTuple.Item1},Sign:{signTuple.Item3}";
                }
                throw new Exception(msg);
            }

        }

        private Tuple<long, string, string> SetupAuthHeader(RestRequest request, Logistics_Channel channel)
        {
            string key = channel.ApiKey;
            string secret = channel.ApiSecret;
            long timestamp = Timestamp.GetUnixLong();
            string sign = CreateSign(key, secret, timestamp, out string rawString);

            request.AddHeader("AccessKeyID", key);
            request.AddHeader("timestamp", timestamp.ToString());
            request.AddHeader("sign", sign);
            return new Tuple<long, string, string>(timestamp, rawString, sign);
        }

        public async Task<byte[]> DrawPdfLabel(UploadResult result, Logistics_Channel channel, Order_Order order, List<Order_OrderGoods> orderGoods)
        {
            byte[] data = await Task.Run(() =>
            {
                string url = result.DownloadUrl;
                WebClient wc = new WebClient();
                var data = wc.DownloadData(url);
                return data;
            });
            return data;
        }

        public Task<string> GetTrackingNumber(string SystemNo, string TrackingNumber, string ServiceOrderNo, Logistics_Channel channel)
        {
            throw new NotImplementedException();
        }
    }
}
