using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XYY.Common.Standard.String;
using XYY.Model.Standard.Channel;
using XYY.Model.Standard.Order;
using XYY.Service.Standard.ChannelApi.Base;
using RestSharp;
using XYY.Core.Standard.PDF;
using XYY.Service.Standard.ChannelApi.AddressHelper;
using XYY.Core.Standard.ExtendMethods;

namespace XYY.Service.Standard.ChannelApi.Ultidel
{
    public class Api : IBaseApi, IUItidelApi
    {
        public string ServiceName => "UltidelIB";
        private string AccountId = "100001";

        public void CannelOrders(List<string> systemNO)
        {

        }

        public void CannelOrdersByModel(logistics_ChannelCancel logistics_ChannelCancel)
        {
            string clientKey = logistics_ChannelCancel.ApiKey;
            string secretKey = logistics_ChannelCancel.ApiSecret;
            string api = logistics_ChannelCancel.ApiHost;
            long timeStamp = GetTimeStamp();
            string timeStampVal = timeStamp.ToString();
            string url = "/api/oms/parcel-label/v1/cancel";

            RestClient client = new RestClient(api);
            RestRequest request = new RestRequest(url, Method.POST);


            var timeStampStr = GetTimeStamp().ToString();
            CanelSmallPackRequestData requestData = new CanelSmallPackRequestData
            {
                customerAccountId = AccountId,
                customerTrackingID = logistics_ChannelCancel.SystemNo,
                transmissionDateTime = timeStampStr
            };

            CanelSmallPackRequest requestParam = new CanelSmallPackRequest
            {
                data = requestData,
            };

            string requestBody = Newtonsoft.Json.JsonConvert.SerializeObject(requestParam);
            string auth = CreateVAuthorization(url, clientKey, secretKey, requestBody);
            SetHeader(ref request, auth, clientKey, timeStampVal);

            request.AddJsonBody(requestParam);
            var response = client.Execute<ResponseBase>(request);

            if (response.StatusCode != System.Net.HttpStatusCode.OK)
                throw new Exception($"撤单失败,错误状态码:{response.StatusCode}");
            if (response.Data == null)
                throw new Exception($"撤单失败,API返回数据为空");
            if (response.Data.code != 0)
                throw new Exception($"撤单失败,API返回错误代码:{response.Data.code}");
        }

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

        private void SetHeader(ref RestRequest request, string auth, string clientKey, string timeStamp)
        {
            request.AddHeader("vAuthorization", auth);
            request.AddHeader("ClientKey", clientKey);
            request.AddHeader("TimeStamp", timeStamp);
        }

        public async Task<UploadResult> DataUpload(Order_Order order, List<Order_OrderGoods> goods, Logistics_Channel channel, ExtendArg arg)
        {
            string clientKey = channel.ApiKey;
            string secretKey = channel.ApiSecret;
            string api = channel.ApiHost;
            string url = "/api/oms/parcel-label/v1/create";
            long timeStamp = GetTimeStamp();
            string timeStampVal = timeStamp.ToString();
            SmallPackRequest requestParam = CreateSmallPackRequest(order, goods, channel, timeStamp);
            string requestBody = Newtonsoft.Json.JsonConvert.SerializeObject(requestParam);

            string auth = CreateVAuthorization(url, clientKey, secretKey, requestBody);


            RestClient client = new RestClient(api);
            RestRequest request = new RestRequest(url, Method.POST);
            SetHeader(ref request, auth, clientKey, timeStampVal);
            request.AddJsonBody(requestParam);
            var response = await client.ExecuteAsync<SmallPackResult>(request);

            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                string msg = response.ErrorMessage ?? response.Content;
                throw new Exception($"渠道API请求失败，错误代码:{response.StatusCode},信息:{msg}");
            }

            var result = response.Data;
            if (result == null)
                throw new Exception($"渠道API请求失败，结果为空");
            if (result.code != 0)
            {
                string msg = result.message;
                if (!string.IsNullOrWhiteSpace(response.Content))
                {
                    msg += response.Content;
                }
                throw new Exception($"渠道API请求失败，业务错误码:{result.code},错误消息:{msg}");
            }


            var resultBody = result.data;
            if (resultBody == null)
                throw new Exception($"渠道API请求失败，返回结果数据为空");

            if (string.IsNullOrWhiteSpace(resultBody.LMTrackingNo))
                throw new Exception($"渠道API请求失败，尾程跟踪号为空");
            if (string.IsNullOrWhiteSpace(resultBody.parcelLabel))
                throw new Exception($"渠道API请求失败，面单数据为空");

            var labelData = Convert.FromBase64String(resultBody.parcelLabel);

            UploadResult uploadResult = new UploadResult
            {
                LabelData = labelData,
                TrackingNumber = resultBody.LMTrackingNo,
                ServiceOrderNumber = resultBody.sellerOrderNo,
                ServiceOrderNumber2 = resultBody.opCenterCode,
                destinationCode = resultBody.sortingCode1,
                IsSuccess = true
            };
            return uploadResult;
        }



        private SmallPackRequest CreateSmallPackRequest(Order_Order order, List<Order_OrderGoods> goods, Logistics_Channel channel, long timeStamp)
        {
            new USAddressCheck().CheckUSOrder(order);
            string msgId = CreateMessageId();

            SmallPackRequestBody data = CreateSmallPackRequestBody(order, goods, channel, timeStamp);
            SmallPackRequest request = new SmallPackRequest
            {
                messageId = msgId,
                timestamp = timeStamp,
                data = data
            };
            return request;
        }

        private SmallPackRequestBody CreateSmallPackRequestBody(Order_Order order, List<Order_OrderGoods> goods, Logistics_Channel channel, long timeStamp)
        {
            var value = goods.Sum(i => i.DeclareFee * i.Quantity);

            List<ShipmentPackageItemDetail> details = CreatePackDetails(order, goods);

            var firstGoods = goods.First();

            int? opWeight = order.OPWeight;
            if (opWeight == null || opWeight <= 0)
            {
                throw new Exception("当前渠道要求准确实重，但订单信息中缺少实际重量");
            }

            string[] addresses = order.ReceiverStreet.Trim().ToArray(35);
            if (addresses.Length > 2)
                throw new Exception("地址超长");
            string street1 = addresses[0];
            string street2 = addresses.Length > 1 ? addresses[1] : null;

            SmallPackRequestBody smallPackRequestBody = new SmallPackRequestBody
            {
                customerAccountID = AccountId,
                customerTrackingID = order.SystemNo,
                sellerOrderNo = order.SystemNo,
                productCode = channel.ServiceChannelCode,
                pickUpZone = "Ultidel仓",
                shipFromName = "高卓",
                shipFromAddressLine1 = "白沙工业园452号",
                shipFromCounty = "白云区",
                shipFromCity = "广州市",
                shipFromState = "广东省",
                shipFromZip = "520000",
                shipFromCountry = channel.SenderCountryCode,
                shipFromContactPhone = channel.SenderPhone,
                consigneeName = order.ReceiverName,
                consigneeAddressLine1 = street1,
                consigneeAddressLine2 = street2,
                consigneeCity = order.ReceiverCity,
                consigneeState = order.ReceiverState,
                consigneeZip = order.ReceiverZipCode,
                consigneeCountry = order.ReceiverCountryCode,
                termsOfSale = "DDP",
                containBattery = false,
                shipmentPackageActualGrossWeight = opWeight.Value,
                shipmentPackageActualGrossWeightUnit = "g",
                shipmentPackageTotalValueCurrency = "USD",
                shipmentPackageTotalDeclaredValue = value.ToString("f2"),
                shipmentPackageTotalValue = value.ToString("f2"),
                transmissionDateTime = timeStamp,
                packageDescriptionExport = firstGoods.CnName,
                packageDescriptionImport = firstGoods.EnName,
                shipmentPackageTotalDeclaredValueCurrency = "USD",
                shipmentPackageItemDetail = details
            };
            return smallPackRequestBody;
        }

        private List<ShipmentPackageItemDetail> CreatePackDetails(Order_Order order, List<Order_OrderGoods> goods)
        {
            List<ShipmentPackageItemDetail> details = new List<ShipmentPackageItemDetail>();
            foreach (var g in goods)
            {
                ShipmentPackageItemDetail detail = new ShipmentPackageItemDetail
                {
                    itemSKU = g.GoodsNumber,
                    itemQuantity = g.Quantity,
                    itemDescriptionExport = g.CnName,
                    itemDescriptionImport = g.EnName,
                    itemUnitPrice = g.DeclareFee.ToString("f2"),
                    itemUnitPriceCurrency = g.DeclareCurrency,
                    harmonizedTariffNumber = g.HsCode
                };
                details.Add(detail);
            }
            return details;
        }

        private string CreateMessageId()
        {
            return Guid.NewGuid().ToString("n");
        }

        public long GetTimeStamp()
        {
            var now = DateTime.UtcNow;
            long stamp = new DateTimeOffset(now).ToUnixTimeSeconds();
            return stamp;
        }

        public async Task<byte[]> DrawPdfLabel(UploadResult result, Logistics_Channel channel, Order_Order order, List<Order_OrderGoods> orderGoods)
        {
            return await Task.Run(() =>
            {
                //var data = result.LabelData;
                //string transferNumber = order.TransferNumber;
                var data = result.LabelData;
                string tag = !string.IsNullOrEmpty(result.destinationCode) ? result.destinationCode.Substring(result.destinationCode.Length - 3) : GetTag(channel.Id);
                //var newData = PDFHelper.Watermark(data, tag, 1, 10, 10, 24, iTextSharp.text.BaseColor.BLACK);
                return data;
            });
        }

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

        private string CreateVAuthorization(string api, string clientKey, string secretKey, string requestBody)
        {
            string raw = $"{api}|{requestBody}|{clientKey}|{secretKey}";
            string auth = Encryption.Md532(raw);
            return auth;
        }

        public async Task<ChannelBagLabelResult> GetChannelBagLabel(ChannelBagLabelParam param)
        {
            var channel = param.Channel;
            string clientKey = channel.ApiKey;
            string secretKey = channel.ApiSecret;
            string api = channel.ApiHost;
            long timeStamp = GetTimeStamp();
            string timeStampVal = timeStamp.ToString();
            string url = "/api/oms/container-label/v1/create";

            string msgId = CreateMessageId();

            RestClient client = new RestClient(api);
            RestRequest request = new RestRequest(url, Method.POST);

            int quantity = param.BoxDetails.Count();
            string opCenterCode = param.Orders.First().ServiceOrderNumber2;

            var invalidOrder = param.Orders.FirstOrDefault(i => (i.ActualWeight??0) <= 0);
            if (invalidOrder != null)
            {
                throw new Exception($"订单没有实际重量：{invalidOrder.TransferNumber}");
            }

            var weight = param.Orders.Sum(o => (o.ActualWeight??0));
            List<packageDetail> packageDetails = new List<packageDetail>();

            foreach (var i in param.Orders)
            {
                packageDetail pd = new packageDetail
                {
                    customerTrackingID = i.SystemNo
                };
                packageDetails.Add(pd);
            }

            GetBagLabelRequestData data = new GetBagLabelRequestData
            {
                containBattery = false,
                containerActualGrossWeight = weight,
                containerActualGrossWeightUnit = "g",
                containerPackageQuantity = quantity,
                customerAccountId = AccountId,
                customerContainerTrackingId = param.Box.BoxNumber,
                opCenterCode = opCenterCode,
                transmissionDateTime = timeStamp,
                packageDetail = packageDetails
            };

            GetBagLabelRequest getBagLabelRequest = new GetBagLabelRequest
            {
                messageId = msgId,
                timestamp = timeStamp,
                data = data
            };

            string requestBody = Newtonsoft.Json.JsonConvert.SerializeObject(getBagLabelRequest);
            string auth = CreateVAuthorization(url, clientKey, secretKey, requestBody);
            SetHeader(ref request, auth, clientKey, timeStampVal);

            request.AddJsonBody(getBagLabelRequest);
            var response = await client.ExecuteAsync<GetPackLabelResponse>(request);

            if (response.StatusCode != System.Net.HttpStatusCode.OK)
                throw new Exception($"获取大包面单失败,错误状态码:{response.StatusCode},{response.Content}");
            if (response.Data == null)
                throw new Exception($"获取大包面单失败,API返回数据为空");
            if (response.Data.code != 0)
            {
                string errMsg = response.Data.message;
                throw new Exception($"获取大包面单失败,API返回错误代码:{response.Data.code},错误信息:{errMsg},{response.Content}");
            }




            GetPackLabelResponseData respData = response.Data.data;
            var label1Data = respData.containerLabel1;
            if (string.IsNullOrEmpty(label1Data))
            {
                throw new Exception("大包标签数据为空");
            }


            var bytes = Convert.FromBase64String(label1Data);

            var newBytes = PDFHelper.Watermark(bytes, param.Box.BoxNumber, 1, 200, 40, 11, iTextSharp.text.BaseColor.BLACK);
            var base64 = Convert.ToBase64String(newBytes);
            ChannelBagLabelResult result = new ChannelBagLabelResult
            {
                Label = base64,
                BoxNumber = param.Box.BoxNumber,
                ChannelBagNumber = respData.MMContainerTrackingID,
                DestinationPortCode = respData.destinationPortCode,
                DestinationTurnOverCode = respData.destinationTurnOverCode,
                ServiceCode = respData.serviceCode
            };
            return result;
        }

        private string GetTag(int channelId)
        {
            if (channelId == 309) return "1L";
            if (channelId == 337) return "1H";
            if (channelId == 338) return "2L";
            if (channelId == 339) return "2H";
            return "";
        }



        public async Task<List<BagDeclareErrosResult>> BagDeclare(BagDeclareRequest bagDeclareRequest)
        {
            List<BagDeclareErrosResult> resultList = new List<BagDeclareErrosResult>();

            var allDetails = bagDeclareRequest.BatchDeclare.SelectMany(i => i.Value);
            var ladingBillGroups = allDetails.GroupBy(d => d.LadingBillNumber);

            var channel = bagDeclareRequest.Channel;

            foreach (var group in ladingBillGroups)
            {
                var ladingBillNumber = group.Key;
                var boxNumbers = group.Select(d => d.BoxNumber).Distinct();
                var boxDetail = group.First();

                string destCountry = boxDetail.CountryCode;
                //string originAirPort = (channel.Id == 340 && string.IsNullOrWhiteSpace(boxDetail.OriginAirPort)) ? "CAN" : boxDetail.OriginAirPort;
                //string destAirPort = (channel.Id == 340 && string.IsNullOrWhiteSpace(boxDetail.DestAirPort)) ? "LAX" : boxDetail.DestAirPort;
                GetChannelPort(boxDetail,bagDeclareRequest.Channel, out string originAirPort, out string destAirPort);
                if (string.IsNullOrWhiteSpace(originAirPort))
                {
                    throw new Exception("出发口岸为空");
                }
                if (string.IsNullOrEmpty(destAirPort))
                {
                    throw new Exception("目的地口岸为空");
                }
                try
                {
                    await SubmitByLadingBill(channel, boxNumbers, ladingBillNumber, destCountry, originAirPort, destAirPort);
                }
                catch (Exception ex)
                {
                    var errItems = boxNumbers.Select(i => new BagDeclareErrosResult { BoxNumber = i, ErrorMessage = ex.Message });
                    resultList.AddRange(errItems);
                }
            }
            return resultList;
        }

        private void GetChannelPort(BoxDetailItem boxDetail, Logistics_Channel channel, out string originAirPort, out string destAirPort)
        {
            originAirPort = boxDetail.OriginAirPort;// (channel.Id == 340 && string.IsNullOrWhiteSpace()) ? "CAN" : boxDetail.OriginAirPort;
            destAirPort = boxDetail.DestAirPort;// (channel.Id == 340 && string.IsNullOrWhiteSpace()) ? "LAX" : boxDetail.DestAirPort;
            if (string.IsNullOrEmpty(originAirPort)) originAirPort = "CAN";

           
            if (string.IsNullOrEmpty(destAirPort))
            {
                if (channel.Id == 340) destAirPort = "LAX";
                else if (channel.Id == 407) destAirPort= "JFK";
                else if (channel.Id == 408) destAirPort= "LAX";
                else if (channel.Id == 409) destAirPort= "ORD";
                else
                {
                    if (channel.PublicCode.Contains("LAX", StringComparison.OrdinalIgnoreCase)) destAirPort = "LAX";
                    else if (channel.PublicCode.Contains("JFK", StringComparison.OrdinalIgnoreCase)) destAirPort = "JFK";
                    else if (channel.PublicCode.Contains("ORD", StringComparison.OrdinalIgnoreCase)) destAirPort = "ORD";
                }
            }
        }

        public async Task SubmitByLadingBill(Logistics_Channel channel, IEnumerable<string> boxNumbers, string ladingBillNumber, string destCountry, string originAirPort, string destAirPort)
        {
            string clientKey = channel.ApiKey;
            string secretKey = channel.ApiSecret;
            string api = channel.ApiHost;
            long timeStamp = GetTimeStamp();
            string timeStampVal = timeStamp.ToString();
            string url = "/api/oms/departure-forecast/v1/create";

            RestClient client = new RestClient(api);
            RestRequest request = new RestRequest(url, Method.POST);


            var containerDetail = boxNumbers.Select(boxNumber => new ContainerDetail { customerContainerTrackingID = boxNumber }).ToList();

            int boxQty = boxNumbers.Count();
            var arrivalCountry = destCountry;
            var arrivalPort = destAirPort;
            var departPort = originAirPort;


            string msgId = CreateMessageId();

            SubmitRequestData requestData = new SubmitRequestData
            {
                containerDetail = containerDetail,
                arrivalCountry = arrivalCountry,
                arrivalPortCode = arrivalPort,
                containerQuantity = boxQty,
                ATA = null,
                ATD = null,
                ETA = DateTime.Now.ToString("s"),
                ETD = DateTime.Now.AddDays(1).ToString("s"),
                cleaningModel = null,
                customerAccountID = AccountId,
                departureCountry = "CN",
                departurePortCode = departPort,
                MAWB = ladingBillNumber,
                transmissionDateTime = timeStamp
            };

            SubmitRequest requestParam = new SubmitRequest
            {
                messageId = msgId,
                timestamp = timeStamp,
                data = requestData,
            };

            string requestBody = Newtonsoft.Json.JsonConvert.SerializeObject(requestParam);
            string auth = CreateVAuthorization(url, clientKey, secretKey, requestBody);
            SetHeader(ref request, auth, clientKey, timeStampVal);

            request.AddJsonBody(requestParam);
            var response = await client.ExecuteAsync<ResponseBase>(request);
            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                string msg = response.Content ?? "失败";
                throw new Exception($"API请求失败:{msg}");
            }
            if (response.Data.code != 0)
            {
                string msg = response.Content;
                throw new Exception($"API返回错误代码:{response.Data.code}:{msg}");
            }

        }

        public async Task CannelBag(CannelIBBagParam param)
        {
            var channel = param.Channel;
            string clientKey = channel.ApiKey;
            string secretKey = channel.ApiSecret;
            string api = channel.ApiHost;
            long timeStamp = GetTimeStamp();
            string timeStampVal = timeStamp.ToString();
            string url = "/api/oms/container-label/v1/cancel";

            var requestParam = new LabelCannelRequestData
            {
                customerAccountId = AccountId,
                customerContainerTrackingId = param.BoxNumber,
                transmissionDateTime = timeStamp
            };

            RestClient client = new RestClient(api);
            RestRequest request = new RestRequest(url, Method.POST);
            string requestBody = Newtonsoft.Json.JsonConvert.SerializeObject(requestParam);
            string auth = CreateVAuthorization(url, clientKey, secretKey, requestBody);
            SetHeader(ref request, auth, clientKey, timeStampVal);
            request.AddJsonBody(requestParam);

            var response = await client.ExecuteAsync<ResponseBase>(request);
            if (response.StatusCode != System.Net.HttpStatusCode.OK)
            {
                string msg = response.Content ?? "失败";
                throw new Exception($"API请求失败:{msg}");
            }
            if (response.Data.code != 0)
            {
                string msg = response.Data.message;
                throw new Exception($"API返回错误代码:{response.Data.code}:{msg}");
            }
        }
    }
}
