using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using XYY.Model.Standard.Channel;
using XYY.Model.Standard.Order;
using XYY.Service.Standard.ChannelApi.Base;

namespace XYY.Service.Standard.ChannelApi.UBI
{
    public partial class Api : IBaseApi
    {
        ILogger<Api> _logger;
        public Api(ILogger<Api> logger)
        {
            _logger = logger;
        }

        public string ServiceName => "UBI";

        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)
        {
            if (order.ReceiverCountryCode == "CA")
            {
                order.DestinationPortCode = GetCADestPort(order.ReceiverZipCode);
            }
            var postOrder = ConvertOrder(order, goods, channel);
            string message = await Send(channel, new List<PostOrder> { postOrder }, "POST", "/services/shipper/orders");
            OrderResult orderResult = null;
            try
            {
                 orderResult = Newtonsoft.Json.JsonConvert.DeserializeObject<OrderResult>(message);
            }
            catch (Exception ex)
            {
                throw new Exception($"解析响应失败:{message}");
            }
            if(orderResult == null)
            {
                throw new Exception($"解析响应失败,结果为空");
            }

            if (orderResult.status != "Success")
            {
                if (orderResult != null && orderResult.errors != null && orderResult.errors[0].message.Contains(" is not unique"))
                {

                    var result = await GetLabelResult(new UploadResult { TrackingNumber = order.SystemNo }, channel);
                    return new UploadResult
                    {
                        IsSuccess = true,
                        TrackingNumber = result.data[0].trackingNo,
                        ServiceOrderNumber = result.data[0].orderId,
                        ServiceOrderNumber2 = order.SystemNo,
                        destinationCode = order.DestinationPortCode
                    };
                }
                throw new Exception(string.Join(';', orderResult.errors.Select(x => x.message).ToArray()));
            }
            return new UploadResult
            {
                IsSuccess = true,
                TrackingNumber = orderResult.data[0].trackingNo,
                ServiceOrderNumber = orderResult.data[0].orderId,
                ServiceOrderNumber2 = orderResult.data[0].referenceNo,
                destinationCode = order.DestinationPortCode
            };
        }

        private readonly List<string> yyz = new List<string> { "A", "B", "C", "E", "G", "H", "J", "K", "L", "M", "N", "P" };
        private readonly List<string> yvr = new List<string> { "R", "S", "T", "V", "X", "Y" };

        private string GetCADestPort(string zipCode)
        {
            if (string.IsNullOrWhiteSpace(zipCode)) return string.Empty;
            else
            {
                string prefix = zipCode.Substring(0, 1).ToUpper();
                if (yyz.Contains(prefix))
                    return "YYZ";
                else
                {
                    if (yvr.Contains(prefix))
                        return "YVR";
                    else return string.Empty;
                }
            }
        }

        /// <summary>
        /// 某些客户需要将客单号放到面单SKU里方便分拣
        /// </summary>
        /// <param name="customerId"></param>
        /// <returns></returns>
        private bool PutCustomerOrderNoToSku(int customerId)
        {
            return (customerId == 250 || customerId == 227);
        }

        private PostOrder ConvertOrder(Order_Order order, List<Order_OrderGoods> orderGoods, Logistics_Channel channel)
        {
            if (orderGoods.Sum(x => x.DeclareFee * x.Quantity) > 15)
            {
                if (order.ReceiverCountryCode == "CA")
                    throw new Exception("申报金额不可大于15 USD");
            }


            string orderSku = orderGoods.First().GoodsNumber;

            if (PutCustomerOrderNoToSku(order.CustomerId))
            {
                orderSku = order.CustomerOrderNo;
            }


            PostOrder postOrder = new PostOrder
            {
                city = order.ReceiverCity,
                state = order.ReceiverState,
                addressLine1 = order.ReceiverStreet,
                country = order.ReceiverCountryCode,
                referenceNo = order.SystemNo,
                description = orderGoods.First().EnName,
                nativeDescription = orderGoods.First().CnName,
                facility = "CAN",
                invoiceCurrency = orderGoods.First().DeclareCurrency,
                dimensionUnit = "",
                invoiceValue = orderGoods.Sum(x => x.Quantity * x.DeclareFee),
                authorityToLeave = "",
                height = 1,
                width = 1,
                length = 1,
                phone = order.ReceiverPhone,
                platform = "",
                postcode = order.ReceiverZipCode,
                volume = 1,
                weightUnit = "",
                weight = (order.CustomerWeight / 1000.0),
                incoterm = "",
                lockerService = "",
                serviceCode = "ubi.asp.cn2au.aupost",
                serviceOption = "",
                sku = orderSku,
                recipientName = order.ReceiverName,
                recipientTaxId = order.TaxNo,
                recipientCompany = "",
                orderItems = new List<OrderItemsItem>(),
                shipperAddressLine1 = "",
                shipperAddressLine2 = "",
                shipperAddressLine3 = "",
                shipperCity = "",
                shipperCountry = "",
                shipperName = "",
                email=order.ReceiverEmail,
            };
            double goodsWeight = Math.Round(order.CustomerWeight / 1000.0 / (double)orderGoods.Sum(x => x.Quantity), 3);
            postOrder.weight = goodsWeight * orderGoods.Sum(x => x.Quantity); ;
            foreach (var item in orderGoods)
            {
                postOrder.orderItems.Add(new OrderItemsItem
                {
                    hsCode = item.HsCode,
                    itemNo = item.GoodsNumber,
                    itemCount = item.Quantity,
                    weight = goodsWeight,
                    sku = PutCustomerOrderNoToSku(order.CustomerId) ? order.CustomerOrderNo : item.GoodsNumber,
                    description = item.EnName,
                    nativeDescription = item.CnName,
                    unitValue = item.DeclareFee,
                    originCountry = "CN"
                });
            }

            postOrder.serviceCode = channel.ServiceChannelCode;
            postOrder.facility = string.Empty;

            return postOrder;
        }


        public async Task<byte[]> DrawPdfLabel(UploadResult result, Logistics_Channel channel, Order_Order order, List<Order_OrderGoods> orderGoods)
        {
            LabelResult labelResult = await GetLabelResult(result, channel);
            if (labelResult.status == "Success")
            {
                if (labelResult.data[0].status != "Success")
                {
                    throw new Exception(labelResult.data[0].errors);
                }
                else
                {
                    string base64 = labelResult.data[0].labelContent;
                    byte[] data = Convert.FromBase64String(base64);
                    return data;
                }

            }
            else
            {
                throw new Exception(labelResult.errors);
            }
        }

        private async Task<LabelResult> GetLabelResult(UploadResult result, Logistics_Channel channel)
        {
            LaeblRequest laeblRequest = new LaeblRequest
            {
                labelFormat = "pdf",
                labelType = "1",
                orderIds = new List<string> { result.TrackingNumber },
                packinglist = "false",
                merged = "false"
            };
            string content = await Send(channel, laeblRequest, "POST", "/services/shipper/labels");
            LabelResult labelResult = Newtonsoft.Json.JsonConvert.DeserializeObject<LabelResult>(content);
            return labelResult;
        }

        private async Task<string> Send(Logistics_Channel channel, object data, string meth, string url)
        {
            string gmt = DateTime.UtcNow.ToString("r");
            //string gmt = "Tue,06May 201421:20:27GMT";
            return await PostJsonETowerOrder(meth, data, url, "application/json", channel.ApiHost, channel.ApiKey, channel.ApiSecret);

        }

        public async Task<string> PostJsonETowerOrder(string smethod, object s, string path, string accept, string host, string token, string key)
        {
            //解决出现the SSL connection could not be established的问题
            ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
            string wRespStatusCode = string.Empty;
            string responseText = string.Empty;
            bool bwaserror = false;
            string WALLTECH_SERVER = host + path;
            string ACCESS_TOKEN = token;
            string SECRET_KEY = key;

            var http = (HttpWebRequest)WebRequest.Create(new Uri(WALLTECH_SERVER));
            //http.Proxy = new WebProxy("http://127.0.0.1:8888");
            http.Accept = accept;
            http.ContentType = "application/json";
            http.Method = smethod;
            if (path == "/services/shipper/handover")
                http.Timeout = 60 * 1000 * 10;
            string walltech_date = DateTime.UtcNow.ToString("r");
            StringBuilder auth = new StringBuilder();
            auth.Append(smethod);
            auth.Append("\n");
            auth.Append(walltech_date);
            auth.Append("\n");
            auth.Append(host + path);

            string hash = EncodeHMAC(auth.ToString(), Encoding.UTF8.GetBytes(SECRET_KEY));

            http.Headers.Add("X-WallTech-Date", walltech_date);
            http.Headers.Add("Authorization", "WallTech " + ACCESS_TOKEN + ':' + hash);
            Stream newStream = null;
            Byte[] bytes;
            http.KeepAlive = false;
            var encoding = new UTF8Encoding();
            if (s != null)
            {
                string sJSON = Newtonsoft.Json.JsonConvert.SerializeObject(s);
                bytes = encoding.GetBytes(sJSON);

                newStream = http.GetRequestStream();
                newStream.Write(bytes, 0, bytes.Length);
                newStream.Close();
            }

            HttpWebResponse response = null;

            try
            {
                response = (HttpWebResponse)await http.GetResponseAsync();
            }
            catch (WebException we)
            {
                if (we.Response != null)
                {
                    wRespStatusCode = ((HttpWebResponse)we.Response).StatusCode.ToString();
                    bwaserror = true;
                    using (var reader = new System.IO.StreamReader(we.Response.GetResponseStream(), encoding))
                    {
                        responseText = reader.ReadToEnd();
                        return responseText;
                    }
                }
                else
                {
                    return we.Message+$",{we.InnerException?.Message}";
                }
            }

            if (bwaserror == false)
            {
                if (wRespStatusCode == "")
                {
                    wRespStatusCode = response.StatusCode.ToString();
                }

                using (var reader = new System.IO.StreamReader(response.GetResponseStream(), encoding))
                {
                    string data = reader.ReadToEnd();
                    return data;
                }
            }
            try
            {
                response.Close();
            }
            catch { }
            try
            {
                if (newStream != null)
                    newStream.Dispose();
            }
            catch { }
            try
            {
                response.Dispose();
            }
            catch { }
            try
            {
                bytes = null;
            }
            catch { }

            return "";
        }

        private string EncodeHMAC(string input, byte[] key)
        {
            byte[] hashedValue;
            using (HMACSHA1 hmac = new HMACSHA1(key))
            {
                byte[] stringBytes = Encoding.UTF8.GetBytes(input);
                hashedValue = hmac.ComputeHash(stringBytes);
            }
            return Convert.ToBase64String(hashedValue);
        }

        public class WeightRequest
        {
            public string orderId
            {
                get; set;
            }

            public int weight
            {
                get; set;
            }

            public string weightUnit
            {
                get; set;
            }

        }

        public async Task<List<BagDeclareErrosResult>> UpdateWeight(BagDeclareRequest bagDeclareRequest)
        {
            List<BagDeclareErrosResult> errors = new List<BagDeclareErrosResult>();
            List<WeightRequest> requests = new List<WeightRequest>();
            foreach (var item in bagDeclareRequest.BatchDeclare)
            {
                foreach (var val in item.Value)
                {
                    requests.Add(new WeightRequest { orderId = val.TrackingNumber, weightUnit = "G", weight = val.Weight });
                }
            }
            for (int i = 0; i < Math.Ceiling(requests.Count / 30.0); i++)
            {
                string str = await Send(bagDeclareRequest.Channel,
                    requests.Skip(i * 30).Take(30), "POST", "/services/shipper/update-order");
                var result = Newtonsoft.Json.JsonConvert.DeserializeObject<BagResult>(str);
                if (result.errors != null && result.errors.Count > 0)
                {
                    //_logger.LogError("UBI-奥睿德重量更新失败 {@result}", result);
                    //foreach (var item in bagDeclareRequest.BatchDeclare)
                    //{
                    //    errors.AddRange(item.Value.Select(x => x.TrackingNumber));
                    //}
                }
                else
                {
                    foreach (var item in result.data)
                    {
                        if (item.errors != null && item.errors.Count > 0)
                        {
                            _logger.LogError("UBI-重量更新成功 {@result}", item);
                            errors.Add(new BagDeclareErrosResult
                            {
                                TrackingNumber = item.trackingNo,
                                ErrorMessage = item.errors.First().message
                            });
                        }
                    }
                }
            }

            return errors;
        }

        /// <summary>
        /// 重量更新加大包推送
        /// </summary>
        /// <param name="bagDeclareRequest"></param>
        /// <returns></returns>
        public async Task<List<BagDeclareErrosResult>> BagDeclare(BagDeclareRequest bagDeclareRequest)
        {
            List<BagDeclareErrosResult> errors = new List<BagDeclareErrosResult>();
            //var weightResult = await UpdateWeight(bagDeclareRequest);
            //if (weightResult.Count > 0)
            //{
            //    return weightResult;
            //}
            //else
            //{
            for (int i = 0; i < Math.Ceiling(bagDeclareRequest.BatchDeclare.Count / 1.0); i++)
            {
                string batchNo = DateTime.Now.ToString("yyyyMMddHHmmss");
                string str = await Send(bagDeclareRequest.Channel, new BoxRequest
                {
                    batchNo = batchNo,
                    bags = bagDeclareRequest.BatchDeclare.Skip(i * 1).Take(1).Select(x => new BagsItem
                    {
                        bagNo = x.Key,
                        destCountry = bagDeclareRequest.Country,
                        battery = false,
                        facility = "",
                        serviceCode = bagDeclareRequest.Channel.ServiceChannelCode,
                        sortCode = "",
                        totalWeight = bagDeclareRequest.BatchDeclare.Sum(y => y.Value.Sum(z => z.Weight) / 1000.00),
                        parcels = x.Value.Select(y => new ParcelsItem { trackingNo = y.TrackingNumber }).ToList()
                    }).ToList(),
                    handoverTime = DateTime.UtcNow.ToString("r"),
                    consignee = "gaozuo",
                    consignor = "gaozuo",
                    driver = "gaozuo",
                    plateNbr = "gaozuo",
                    remark = "gaozuo"
                }, "POST", "/services/shipper/handover");
                var result = Newtonsoft.Json.JsonConvert.DeserializeObject<BagResult>(str);
                var err = result.data.Where(x => x.status != "Success").SelectMany(x => x.orderResults.Select(y => new BagDeclareErrosResult
                {
                    TrackingNumber = y.trackingNo,
                    ErrorMessage = y.errors.FirstOrDefault()?.message
                }));
                errors.AddRange(err);
            }
            //}
            return errors;

        }

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

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