using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using XYY.Data.Standard.Order;
using XYY.Model.Standard.Channel;
using XYY.Model.Standard.Order;
using XYY.Service.Standard.ChannelApi.Base;

namespace XYY.Service.Standard.ChannelApi._4PX
{
    public class Api : IBaseApi
    {

        public string ServiceName => "4px";
        IOrder_OrderPrintQtyRepository _order_OrderPrintQtyRepository;
        public Api(IOrder_OrderPrintQtyRepository order_OrderPrintQtyRepository)
        {
            _order_OrderPrintQtyRepository = order_OrderPrintQtyRepository;
        }

        /// <summary>
        /// DateTime时间格式转换为Unix时间戳格式
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        private int ConvertDateTimeInt(System.DateTime time)
        {
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            return (int)(time - startTime).TotalSeconds;
        }

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

        public class ConfimsOrder
        {
            /// <summary>
            /// 
            /// </summary>
            public string msg { get; set; }
            /// <summary>
            /// 
            /// </summary>
            public string result { get; set; }
        }

        public class ConfimRequest
        {
            public string request_no
            {
                get; set;
            }

            public int weight
            {
                get; set;
            }
        }

        public async Task<List<string>> Confims(List<OrderConfimRequest> orderList, Logistics_Channel channel)
        {
            var list = new List<string>();
            foreach (var order in orderList)
            {
                try
                {
                    var result = await Send<dynamic>(channel.ApiHost, channel.ApiKey, channel.ApiSecret, "ds.xms.order.updateweight",
                        new ConfimRequest { request_no = order.OrderNo, weight = (int)order.OPWeight });
                    //await Send<dynamic>(channel.ApiHost, channel.ApiKey, channel.ApiSecret, labelMethod, new { request_no = postData.ref_no });
                    //if (result.result == "0")
                    //    list.Add(order.TrackingNumber);
                }
                catch (Exception ex)
                {
                    string message = ex.Message;
                    Console.WriteLine(message);
                    list.Add(order.TrackingNumber);
                }
            }
            return list;
        }

        string[] _4pxOrders = new string[] {
                "420112189361210914450005548317",
                "420367489361210914350007295021",
                "420601319361210914250008057324",
                "420912059361210914150004550389",
                "420917029361210914150004582663"
        };
        public async Task<UploadResult> DataUpload(Order_Order order, List<Order_OrderGoods> goods, Logistics_Channel channel, ExtendArg arg)
        {
            if (order.ReceiverCountryCode != "GB" && channel.ServiceCode == "O5")
                throw new Exception("该渠道仅可发货英国");
            if (order.ReceiverCountryCode != "GB" && channel.PublicCode == "GBLineEE")
                throw new Exception("该渠道仅可发货英国");
            if (order.ReceiverCountryCode != "DE" && channel.PublicCode == "DELineEE")
            {
                throw new Exception("该渠道仅可发货德国");
            }
            if (channel.ServiceChannelCode == "O5" && order.CustomerWeight > 2000)
                throw new Exception("该渠道仅支持2KG以下派送");
            if (channel.PublicCode == "GBLineEE" && order.CustomerWeight > 2000)
                throw new Exception("该渠道仅支持2KG以下派送");
            if (channel.PublicCode == "DELineEE" && order.CustomerWeight > 3000)
                throw new Exception("该渠道仅支持3KG以下派送");

            if (channel.PublicName == "英国专线3")
            {
                if (order.ReceiverCountryCode != "GB")
                {
                    throw new Exception("该渠道仅可发货英国");
                }

                foreach (var item in goods)
                {
                    if (item.DeclareCurrency == "EUR")
                    {
                        item.DeclareFee = item.DeclareFee * 1.04m;
                        item.DeclareCurrency = "USD";
                    }

                }
            }

            if (channel.PublicName == "美国标准专线")
            {

                if (order.ReceiverCountryCode != "US")
                {
                    throw new Exception("该渠道仅可发货美国");
                }
                else
                {

                    string[] Remote = new string[] { "AK", "HI", "GU", "AS", "MP", "PR", "VI", "AE", "AP" };

                    Dictionary<string, string> pairs = new Dictionary<string, string>
                {
                    { "Washington", "WA" },
                    { "Oregon", "OR" },
                    { "California", "CA" },
                    { "Nevada", "NV" },
                    { "Idaho", "ID" },
                    { "Utah", "UT" },
                    { "Arizona", "AZ" },
                    { "Montana", "MT" },
                    { "Wyoming", "WY" },
                    { "Colorado", "CO" },
                    { "New Mexico", "NM" },
                    { "Wisconsin", "WI" },
                    { "Illinois", "IL" },
                    { "Tennessee", "TN" },
                    { "Mississippi", "MS" },
                    { "Michigan", "MI" },
                    { "Indiana", "IN" },
                    { "Kentucky", "KY" },
                    { "Alabama", "AL" },
                    { "Ohio", "OH" },
                    { "West Virginia", "WV" },
                    { "Virginia", "VA" },
                    { "North Carolina", "NC" },
                    { "South Carolina", "SC" },
                    { "Georgia", "GA" },
                    { "North Dakota", "ND" },
                    { "South Dakota", "SD" },
                    { "Nebraska", "NE" },
                    { "Kansas", "KS" },
                    { "Oklahoma", "OK" },
                    { "Texas", "TX" },
                    { "Minnesota", "MN" },
                    { "Iowa", "IA" },
                    { "Missouri", "MO" },
                    { "Arkansas", "AR" },
                    { "Louisiana", "LA" },
                    { "Florida", "FL" },
                    { "Pennsylvania", "PA" },
                    { "New York", "NY" },
                    { "Washington D. C.", "DC" },
                    { "Maryland", "MD" },
                    { "Delaware", "DE" },
                    { "New Jersey", "NJ" },
                    { "Connecticut", "CT" },
                    { "Rhode Island", "RI" },
                    { "Massachusetts", "MA" },
                    { "Maine", "ME" },
                    { "New Hampshire", "NH" },
                    { "Vermont", "VT" }
                };

                    if (!pairs.Keys.Any(x => x.Equals(order.ReceiverState, StringComparison.CurrentCultureIgnoreCase))
                            && !pairs.Values.Any(x => x.Equals(order.ReceiverState, StringComparison.CurrentCultureIgnoreCase))
                            )
                    {
                        if (pairs.Keys.Any(x => x.Equals(order.ReceiverCity, StringComparison.CurrentCultureIgnoreCase)))
                        {
                            string state = order.ReceiverState;
                            order.ReceiverState = pairs.Where(x => x.Key.Equals(order.ReceiverCity, StringComparison.CurrentCultureIgnoreCase)).First().Value;
                            order.ReceiverCity = state;
                        }
                        else if (pairs.Values.Any(x => x.Equals(order.ReceiverCity, StringComparison.CurrentCultureIgnoreCase)))
                        {
                            string state = order.ReceiverState;
                            order.ReceiverState = order.ReceiverCity;
                            order.ReceiverCity = order.ReceiverState;
                        }
                        else
                        {
                            //throw new Exception("美国专线必须传入标准的州名或州简写");
                        }
                    }
                    else if (pairs.Keys.Any(x => x.Equals(order.ReceiverState, StringComparison.CurrentCultureIgnoreCase)))
                    {
                        order.ReceiverState = pairs.Where(x => x.Key.Equals(order.ReceiverState, StringComparison.CurrentCultureIgnoreCase)).First().Value;
                    }
                    if (Remote.Any(x => x.Equals(order.ReceiverState, StringComparison.CurrentCultureIgnoreCase)))
                    {
                        throw new Exception("美国该地区不支持派送");
                    }
                }
            }

            if (goods.Sum(x => x.Quantity * x.DeclareFee) > 160 && order.ReceiverCountryCode == "GB")
            {
                throw new Exception("英国申报金额不可于160USD");
            }
            if (order.ReceiverName.Length > 32)
            {
                throw new Exception("收件人名称不能超过32个字符");
            }

            string method = "ds.xms.order.create";
            var postData = ConvertOrder(order, goods, channel, arg);
            OrderResult orderResult = null;
            try
            {
                orderResult = await Send<OrderResult>(channel.ApiHost, channel.ApiKey, channel.ApiSecret, method, postData);
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("单号已存在:参考单号"))
                {
                    orderResult = new OrderResult
                    {
                        ds_consignment_no = string.Empty,
                        ref_no = postData.ref_no
                    };
                }
                else
                {
                    throw ex;
                }
            }

            string labelMethod = "ds.xms.label.get";
            System.Threading.Thread.Sleep(2 * 1000);
            string trackingNumber = "";
            string url = string.Empty;
            int i = 0;
            while (string.IsNullOrEmpty(url) && i < 5)
            {
                i++;
                try
                {
                    var result2 = await Send<dynamic>(channel.ApiHost, channel.ApiKey, channel.ApiSecret, labelMethod, new { request_no = postData.ref_no });
                    url = result2.label_url_info.logistics_label;
                    trackingNumber = result2.label_barcode;
                }
                catch (Exception ex)
                {
                    if (ex.Message.ToLower().Contains("please try again later"))
                        System.Threading.Thread.Sleep(2 * 1000);
                    else
                        throw ex;
                }
            }

            if (string.IsNullOrEmpty(url))
            {
                throw new Exception("获取面单与运单失败!面单地址为空");
            }

            return new UploadResult
            {
                TrackingNumber = trackingNumber,
                ServiceOrderNumber = orderResult.ds_consignment_no,
                IsSuccess = true,
                ServiceOrderNumber2 = orderResult.ref_no,
                DownloadUrl = url
            };
        }


        public OrderRequest ConvertOrder(Order_Order order, List<Order_OrderGoods> goods, Logistics_Channel channel, ExtendArg arg)
        {
            if (order.ChannelId == 230)
            {
                if (string.IsNullOrEmpty(order.ReceiverPhone))
                    order.ReceiverPhone = "1234567890";
                else
                {
                    if (order.ReceiverPhone.Length < 10 && !order.ReceiverPhone.Contains("+1"))
                    {
                        order.ReceiverPhone = "+1 " + order.ReceiverPhone;
                    }
                }
            }


            foreach (var item in goods)
            {
                if (item.DeclareCurrency == "USD")
                {
                    if (channel.PublicCode == "DELineEE")
                    {
                        item.DeclareCurrency = "EUR";
                        item.DeclareFee = item.DeclareFee * 0.837m;
                    }
                }
            }
            if (string.IsNullOrEmpty(order.ReceiverState))
            {
                order.ReceiverState = order.ReceiverCity;
            }
            if (!string.IsNullOrEmpty(order.ReceiverPhone) && order.ReceiverPhone.Contains("GB_"))
            {
                order.ReceiverPhone = order.ReceiverPhone.Replace("GB_", "");
            }
            string refNo = order.SystemNo + "_" + _order_OrderPrintQtyRepository.GetPackagePrintQty(order.SystemNo);

            if (_4pxOrders.Contains(order.TrackingNumber))
            {
                refNo = refNo.Replace("XYYEX", "0A");
            }
            if (channel.Id == 263)
                refNo = refNo.Replace("XYYEX", "B");
            else if (channel.Id >= 202)
                refNo = refNo.Replace("XYYEX", "A");

            string vat = null;
            if (!string.IsNullOrWhiteSpace(order.TaxNo))
            {
                vat = order.TaxNo.Replace(" ", "").Replace(".", "");
            }
            OrderRequest orderRequest = new OrderRequest
            {
                ref_no = refNo,
                business_type = "BDS",
                duty_type = "U",
                vat_no = vat,
                return_info = new Return_info()
                {
                    is_return_on_domestic = "Y",
                    is_return_on_oversea = "Y",
                },
                logistics_service_info = new Logistics_service_info
                {
                    logistics_product_code = channel.ServiceChannelCode,
                },
                parcel_list = new List<Parcel_listItem>
                {
                    new Parcel_listItem
                    {
                         weight = order.CustomerWeight,
                         parcel_value = ((double) goods.Sum(x=>x.DeclareFee* x.Quantity)).ToString("0.00"),
                         currency=goods.FirstOrDefault().DeclareCurrency,
                         include_battery = "N",
                         battery_type= string.Empty,
                         declare_product_info = goods.Select(x=> new Declare_product_infoItem{
                             declare_product_name_cn = x.CnName,
                             declare_product_name_en = x.EnName,
                             declare_product_code_qty = x.Quantity,
                             declare_unit_price_export =   x.DeclareFee.ToString("0.00"),
                             currency_export =x.DeclareCurrency,
                             declare_unit_price_import =   x.DeclareFee.ToString("0.00"),
                             currency_import= x.DeclareCurrency,
                             brand_export= "none",
                             brand_import= "none",
                             hscode_import = x.HsCode,
                             hscode_export = x.HsCode
                         }).ToList(),
                         product_list =
                            goods.Select(x=>new Product_listItem{
                                 product_name = x.CnName,
                                 product_description=x.EnName,
                                 product_unit_price = x.DeclareFee.ToString("0.00"),
                                 currency=x.DeclareCurrency,
                                 qty = x.Quantity,
                                 sku_code = x.GoodsNumber

                            }).ToList()
                    }
                },
                is_insure = "Y",
                insurance_info = new Insurance_info(),
                sender = new Sender
                {
                    first_name = channel.SenderName,
                    phone = channel.SenderPhone,
                    post_code = channel.SenderZipCode,
                    country = channel.SenderCountryCode,
                    state = channel.SenderState,
                    city = channel.SenderCity,
                    street = channel.SenderStreet
                },
                recipient_info = new Recipient_info
                {
                    first_name = order.ReceiverName,
                    phone = order.ReceiverPhone,
                    country = order.ReceiverCountryCode,
                    city = order.ReceiverCity,
                    street = order.ReceiverStreet,
                    state = order.ReceiverState,
                    post_code = order.ReceiverZipCode
                },
                deliver_type_info = new Deliver_type_info
                {
                    deliver_type = "1",
                }
            };
            if (channel.PublicCode == "DELineEE"
                || channel.PublicCode == "FRLineEE"
                || channel.PublicCode == "ITLineEE"
                || channel.PublicCode == "ZL-IT-Line"
                )
            {
                if (!string.IsNullOrEmpty(order.ioss))
                {
                    orderRequest.ioss_no = order.ioss;
                }
                else
                {
                    if (arg.AgentIOSS)
                        orderRequest.ioss_no = "IM2760000742";
                }
            }
            if (channel.PublicCode == "DELineEE" && order.CustomerId == 227)
            {
                orderRequest.ioss_no = "IM0000000001";
            }
            return orderRequest;
        }

        public async Task<T> Send<T>(string apiUrl, string app_key, string app_s, string method, object data, string v = "1.0")
        {
            int timestamp = ConvertDateTimeInt(DateTime.Now);
            string format = "json";
            string language = "cn";
            string sign = GetSign(app_key, app_s, method, data, timestamp, format, v);
            var client = new RestSharp.RestClient(apiUrl);
            var request = new RestSharp.RestRequest($"?method={method}&app_key={app_key}&v={v}&timestamp={timestamp}&format={format}&sign={sign}&language={language}"
                , RestSharp.Method.POST);
            request.AddJsonBody(data);
            request.AddHeader("Content-Type", "application/json;");
            request.AddHeader("Accept", "*/*");
            //client.Proxy = new WebProxy("http://localhost:8888/");
            //request.AddHeader("Accept-Encoding", "gzip, deflate, br");

            var response = client.Execute(request);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                var result = Newtonsoft.Json.JsonConvert.DeserializeObject<Result<T>>(response.Content);
                if (result.result == "0")
                {
                    throw new Exception(string.Join(',', result.errors.Select(x => x.error_msg)));
                }
                else
                {
                    return result.data;
                }
            }
            else
            {
                throw new Exception(response.Content ?? response.ErrorMessage);
            }
        }

        private static string ConvertISO88591ToEncoding(string srcString, Encoding dstEncode)
        {

            String sResult;

            Encoding ISO88591Encoding = Encoding.GetEncoding("ISO-8859-1");
            Encoding GB2312Encoding = Encoding.GetEncoding("GB2312"); //这个地方很特殊，必须利用GB2312编码
            byte[] srcBytes = ISO88591Encoding.GetBytes(srcString);

            //将原本存储ISO-8859-1的字节数组当成GB2312转换成目标编码(关键步骤)
            byte[] dstBytes = Encoding.Convert(GB2312Encoding, dstEncode, srcBytes);

            char[] dstChars = new char[dstEncode.GetCharCount(dstBytes, 0, dstBytes.Length)];

            dstEncode.GetChars(dstBytes, 0, dstBytes.Length, dstChars, 0);//利用char数组存储字符
            sResult = new string(dstChars);
            return sResult;

        }
        string format = "json";
        public string GetSign(string app_key, string api_s, string method, object data, int timestamp, string format, string v = "1.0")
        {
            string dataJosn = Newtonsoft.Json.JsonConvert.SerializeObject(data);
            string strS = $"app_key{app_key}format{format}method{method}timestamp{timestamp}v{v}{dataJosn}{api_s}";
            return XYY.Core.Standard.MD5Util.Get(strS, Encoding.UTF8).ToLower();
        }

        public async Task<byte[]> DrawPdfLabel(UploadResult result, Logistics_Channel channel, Order_Order order, List<Order_OrderGoods> orderGoods)
        {
            int minSize = 5 * 1024;
            byte[] data = null;
            for (var i = 0; i < 3; i++)
            {
                try
                {
                    data = await new System.Net.WebClient().DownloadDataTaskAsync(result.DownloadUrl);
                    if (data.Length >= minSize)
                        return data;
                }
                catch
                {

                }
            }
            if (data == null)
            {
                throw new Exception("标签信息有误请联系IT");
            }
            else if (data.Length < minSize)
            {
                throw new Exception("标签内容过小请联系IT");
            }
            else
            {
                return data;
            }
        }

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

        public async Task<string> GetTrackingNumber(string SystemNo, string TrackingNumber, string ServiceOrderNo, Logistics_Channel channel)
        {
            var result = await Send<string>(channel.ApiHost, channel.ApiKey, channel.ApiSecret, "ds.xms.order.get",
                new ConfimRequest { request_no = ServiceOrderNo });
            var r = Newtonsoft.Json.JsonConvert.DeserializeObject<List<TrackingDetail>>(result.Replace("\\", ""));
            return r[0].consignment_info.logistics_channel_no;
        }




    }


    public class Consignment_info
    {
        /// <summary>
        /// 
        /// </summary>
        public string logistics_channel_no { get; set; }
    }

    public class TrackingDetail
    {
        /// <summary>
        /// 
        /// </summary>
        public Consignment_info consignment_info { get; set; }
    }

}
