using Fuel.Payment.Core.Models.UnionPay;
using Fuel.Payment.Service.Factory;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Fuel.Payment.Core.Models;
using System.Xml.Linq;

namespace Fuel.Payment.Service.UnionPayProcessor
{
    public class UnionPayProcessor : AsyncPaymentProcessor
    {
        private readonly NLog.Logger logger = NLog.LogManager.GetLogger("Main");

        /// <summary>
        /// 初始化支付参数
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public override async Task<GenericProcessResponse> Initialize(ElectronicOrderModel order)
        {
            string payXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<UnionPayConfig xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n  <scanUrl>https://mobl-test.chinaums.com/yd-19904</scanUrl>\r\n  <clientId>AC0CF5AAD39942FF8A86754DDE6A431D</clientId>\r\n  <merchantCode>123456789900081</merchantCode>\r\n  <charset>UTF-8</charset>\r\n  <miniUrl>https://mobl-test.chinaums.com/yd-19904/v4/bc/pay</miniUrl>\r\n  <appId>10037ca75e6125aa015e9e12a89b001b</appId>\r\n  <mid>89844175541AAAW</mid>\r\n  <cloudIp>139.196.81.254</cloudIp>\r\n  <terminalCode>00810001</terminalCode>\r\n</UnionPayConfig>";
            UnionPayConfig config = Parse(payXml);
            order.Config = config;
            return new GenericProcessResponse()
            {
                electronicOrderModel = order
            };
        }
        public static UnionPayConfig Parse(string xmlContent)
        {
            XDocument doc = XDocument.Parse(xmlContent);
            UnionPayConfig config = new UnionPayConfig();

            config.scanUrl = doc.Root.Element("scanUrl")?.Value;
            config.clientId = doc.Root.Element("clientId")?.Value;
            config.merchantCode = doc.Root.Element("merchantCode")?.Value;
            config.cloudIp = doc.Root.Element("cloudIp")?.Value;
            config.charset = doc.Root.Element("charset")?.Value; // 直接从XML中获取  
            config.miniUrl = doc.Root.Element("miniUrl")?.Value;
            config.appId = doc.Root.Element("appId")?.Value;
            config.mid = doc.Root.Element("mid")?.Value;
            config.terminalCode = doc.Root.Element("terminalCode")?.Value;

            return config;
        }
        public override async Task<GenericProcessResponse> Process(ElectronicOrderModel order)
        {
            order.TradeStatus = TradeStatus.PAYERROR;
            Dictionary<string, string> payResponse = null;
            try
            {
                logger.Info($"UnionPay is processing, BillNumber {order.BillNumber}, NetAmount {order.NetAmount}, QrCode {order.AuthCode}");
                var config = order.Config as UnionPayConfig;
                var paramDic = new Dictionary<string, string>();
                paramDic.Add("merchantCode", config.merchantCode);
                paramDic.Add("ip", config.cloudIp);
                paramDic.Add("terminalCode", config.terminalCode); //终端号 00810001  XSZZ16UE
                paramDic.Add("deviceType", "05");
                paramDic.Add("transactionAmount", ((int)(order.NetAmount * 100)).ToString());
                paramDic.Add("merchantOrderId", order.BillNumber);
                paramDic.Add("payCode", order.AuthCode);
                string body = JsonConvert.SerializeObject(paramDic, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
                logger.Debug($"UnionPay body: {body}");
                string authorization = Utils.GetAuthorization(config.clientId, config.clientId, body);
                string payUrl = config.scanUrl + "/v4/bc/pay";
                var client = new DefaultClient();
                string rspStr = client.Post(payUrl, authorization, body, config.charset);
                logger.Debug($"UnionPay response: {rspStr}");
                // payResponse = JsonConvert.DeserializeObject<Dictionary<string, string>>(rspStr);

                JObject jsonObject = JObject.Parse(rspStr);
                jsonObject.Remove("rawPromotionList");
                jsonObject.Remove("promotionList");
                string modified_json_string = jsonObject.ToString();
                payResponse = JsonConvert.DeserializeObject<Dictionary<string, string>>(modified_json_string);

                if (payResponse["errCode"] == "00")
                {
                    order.TradeStatus = TradeStatus.SUCCESS;
                }
                else
                {
                    logger.Error($"UnionPay failed due to: {payResponse["errInfo"]}");
                    return new GenericProcessResponse()
                    {
                        AllInPayResponse = payResponse
                    };
                }
            }
            catch (Exception ex)
            {
                logger.Error($"UnionPay exception: {ex}");
            }
            return new GenericProcessResponse()
            {
                AllInPayResponse = payResponse
            };
        }

        public override async Task<GenericProcessResponse> Cancel(ElectronicOrderModel order)
        {
            order.TradeStatus = TradeStatus.PAYERROR;
            Dictionary<string, string> payResponse = null;
            try
            {
                logger.Info($"UnionPay is processing cancel, BillNumber {order.BillNumber}");
                var config = order.Config as UnionPayConfig;
                var paramDic = new Dictionary<string, string>();
                paramDic.Add("merchantCode", config.merchantCode);
                paramDic.Add("terminalCode", config.terminalCode); //终端号
                paramDic.Add("merchantOrderId", order.BillNumber);
                string body = JsonConvert.SerializeObject(paramDic, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
                logger.Debug($"UnionPay body: {body}");
                string authorization = Utils.GetAuthorization(config.clientId, config.clientId, body);
                string payUrl = config.scanUrl + "/erp/bc/reverse";
                var client = new DefaultClient();
                string rspStr = client.Post(payUrl, authorization, body, config.charset);
                logger.Debug($"UnionPay cancel response: {rspStr}");
                payResponse = JsonConvert.DeserializeObject<Dictionary<string, string>>(rspStr);
                if (payResponse["errCode"] == "00")
                {
                    order.TradeStatus = TradeStatus.SUCCESS;
                }
                else
                {
                    logger.Error($"UnionPay cancel failed due to: {payResponse["errInfo"]}");
                    return new GenericProcessResponse()
                    {
                        AllInPayResponse = payResponse
                    };
                }
            }
            catch (Exception ex)
            {
                logger.Error($"UnionPay cancel exception: {ex}");
            }
            return new GenericProcessResponse()
            {
                AllInPayResponse = payResponse
            };
        }

        public override async Task<GenericProcessResponse> Query(ElectronicOrderModel order)
        {
            return await Query(order, 16, 6000);
        }

        public override async Task<GenericProcessResponse> Query(ElectronicOrderModel order, int count, int interval)
        {
            order.TradeStatus = TradeStatus.PAYERROR;
            Dictionary<string, string> payResponse = null;
            try
            {
                logger.Info($"UnionPay is processing query, BillNumber {order.BillNumber}");
                var config = order.Config as UnionPayConfig;
                var paramDic = new Dictionary<string, string>();
                paramDic.Add("merchantCode", config.merchantCode);
                paramDic.Add("terminalCode", config.terminalCode); //终端号
                paramDic.Add("merchantOrderId", order.BillNumber);
                string body = JsonConvert.SerializeObject(paramDic, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
                logger.Debug($"UnionPay body: {body}");
                payResponse = CoreQuery(config, body);
                if (payResponse["errCode"] == "00")
                {
                    if (payResponse["orderStatus"] == "TRADE_SUCCESS")
                    {
                        order.TradeStatus = TradeStatus.SUCCESS;
                    }
                    else
                    {
                        logger.Error($"UnionPay query failed due to: {payResponse["queryResDesc"]}");
                        return new GenericProcessResponse()
                        {
                            AllInPayResponse = payResponse
                        };
                    }
                }
                else
                {
                    logger.Error($"UnionPay query failed due to: {payResponse["errInfo"]}");
                    return new GenericProcessResponse()
                    {
                        AllInPayResponse = payResponse
                    };
                }
            }
            catch (Exception ex)
            {
                logger.Error($"UnionPay query exception: {ex}");
            }
            return new GenericProcessResponse()
            {
                AllInPayResponse = payResponse
            };
        }

        private Dictionary<string, string> CoreQuery(UnionPayConfig config, string body)
        {
            string authorization = Utils.GetAuthorization(config.clientId, config.clientId, body);
            string payUrl = config.scanUrl + "/erp/bc/query";
            var client = new DefaultClient();
            string rspStr = client.Post(payUrl, authorization, body, config.charset);
            logger.Debug($"UnionPay query response: {rspStr}");
            var response = JsonConvert.DeserializeObject<Dictionary<string, string>>(rspStr);
            return response;
        }

        public override async Task<GenericProcessResponse> Return(ElectronicOrderModel order)
        {
            order.TradeStatus = TradeStatus.PAYERROR;
            Dictionary<string, string> payResponse = null;
            try
            {
                logger.Info($"UnionPay is processing refund, BillNumber {order.BillNumber}, " +
                    $"NetAmount {order.NetAmount}, OperatorId {order.OperatorId}");
                var config = order.Config as UnionPayConfig;
                var paramDic = new Dictionary<string, string>();
                paramDic.Add("merchantCode", config.merchantCode);
                paramDic.Add("terminalCode", config.terminalCode); //终端号
                paramDic.Add("merchantOrderId", order.BillNumber);
                paramDic.Add("transactionAmount", ((int)(order.NetAmount * 100)).ToString());
                string body = JsonConvert.SerializeObject(paramDic, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
                logger.Debug($"UnionPay body: {body}");
                string authorization = Utils.GetAuthorization(config.clientId, config.clientId, body);
                string payUrl = config.scanUrl + "/erp/bc/refund";
                var client = new DefaultClient();
                string rspStr = client.Post(payUrl, authorization, body, config.charset);
                logger.Debug($"UnionPay refund response: {rspStr}");
                payResponse = JsonConvert.DeserializeObject<Dictionary<string, string>>(rspStr);
                if (payResponse["errCode"] == "00")
                {
                    order.TradeStatus = TradeStatus.SUCCESS;
                }
                else
                {
                    logger.Error($"UnionPay refund failed due to: {payResponse["errInfo"]}");
                    return new GenericProcessResponse()
                    {
                        AllInPayResponse = payResponse
                    };
                }
            }
            catch (Exception ex)
            {
                logger.Error($"UnionPay refund exception: {ex}");
            }
            return new GenericProcessResponse()
            {
                AllInPayResponse = payResponse
            };
        }

        public override async Task<GenericProcessResponse> UnifiedOrder(ElectronicOrderModel order)
        {
            return new GenericProcessResponse()
            {
                AllInPayResponse = null
            };
        }
    }
}
