using Com.Alipay;
using Com.Alipay.Business;
using Com.Alipay.Domain;
using Com.Alipay.Model;
using Fuel.Payment.Core.Models;
using Fuel.Payment.Service.Factory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using WayneCloud.Models;
using ElectronicOrderModel = Fuel.Payment.Core.Models.ElectronicOrderModel;
using TradeStatus = Fuel.Payment.Core.Models.TradeStatus;


namespace Fuel.Payment.Service.AliPaymentProcessor.Alipay
{
    public class AlipayPaymentProcessor : AsyncPaymentProcessor
    {
        private readonly NLog.Logger _logger = NLog.LogManager.GetLogger("Main");
        private static volatile Dictionary<AliPayConfig, IAlipayTradeService> clients = new Dictionary<AliPayConfig, IAlipayTradeService>();
        public static IAlipayTradeService Clients(AliPayConfig config)
        {
            if (!clients.ContainsKey(config))
            {
                var newClient = F2FBiz.CreateClientInstance(config.serverUrl,
                                                            config.appId,
                                                            config.merchant_private_key,
                                                            config.version,
                                                            config.sign_type,
                                                            config.alipay_public_key,
                                                            config.charset);
                clients.Add(config, newClient);
                return newClient;
            }
            else
            {
                return clients[config];
            }
        }
        public override async Task<GenericProcessResponse> Initialize(ElectronicOrderModel order)
        {
            return new GenericProcessResponse()
            {
                electronicOrderModel = order
            };
        }
        public override async Task<GenericProcessResponse> Process(ElectronicOrderModel order)
        {
            order.TradeStatus = TradeStatus.PAYERROR;
            _logger.Info("Alipay is processing order " + order.BillNumber);
            var config = (AliPayConfig)order.Config;

            AlipayTradePayContentBuilder builder = BuildPayContent(order);
            AlipayF2FPayResult payResult = await Clients(config).tradePay(builder);

            switch (payResult.Status)
            {
                case ResultEnum.SUCCESS:
                    order.TradeStatus = TradeStatus.SUCCESS;
                    break;
                case ResultEnum.FAILED:
                    _logger.Info("Request failed with result: ResultEnum.FAILED");
                    break;
                case ResultEnum.UNKNOWN:
                    _logger.Info("Failed due to unknown reason, plesae check your network configuration and cable connection, " +
                        "then retry with a new order number");
                    break;
            }

            _logger.Info("The final Alipay response is :\r\n" + HttpUtility.UrlDecode(payResult.response?.Body));
            return new GenericProcessResponse()
            {
                AopResponse = payResult.response
            };
        }
        /// <summary>
        /// 构造支付请求数据
        /// </summary>
        /// <returns>请求数据集</returns>
        private AlipayTradePayContentBuilder BuildPayContent(ElectronicOrderModel order)
        {
            //线上联调时，请输入真实的外部订单号。
            string out_trade_no = order.BillNumber;

            var config = (AliPayConfig)order.Config;

            //扫码枪扫描到的用户手机钱包中的付款条码
            AlipayTradePayContentBuilder builder = new AlipayTradePayContentBuilder();

            //收款账号
            builder.seller_id = config.pid;
            //订单编号
            builder.out_trade_no = out_trade_no;
            //支付场景，无需修改
            builder.scene = "bar_code";
            //支付授权码,付款码
            builder.auth_code = order.AuthCode.Trim();
            //订单总金额
            builder.total_amount = order.NetAmount.ToString();
            //参与优惠计算的金额
            //builder.discountable_amount = "";
            //不参与优惠计算的金额
            //builder.undiscountable_amount = "";
            //订单名称
            builder.subject = order.Title;
            //自定义超时时间
            builder.timeout_express = "30m";
            //订单描述
            builder.body = "";
            //门店编号，很重要的参数，可以用作之后的营销
            builder.store_id = order.SiteId;
            //操作员编号，很重要的参数，可以用作之后的营销
            builder.operator_id = order.OperatorId;


            //传入商品信息详情
            List<GoodsInfo> gList = new List<GoodsInfo>();
            order.FuelOrderDetails.ForEach(i =>
            {
                GoodsInfo goods = new GoodsInfo();
                goods.goods_id = i.FuelProductId;
                goods.goods_name = i.FuelProductName;
                goods.price = i.Amount.ToString();
                goods.quantity = i.Qualtity.ToString();
                gList.Add(goods);
            });

            builder.goods_detail = gList;

            //系统商接入可以填此参数用作返佣
            //ExtendParams exParam = new ExtendParams();
            //exParam.sysServiceProviderId = "20880000000000";
            //builder.extendParams = exParam;

            return builder;

        }
        public override async Task<GenericProcessResponse> UnifiedOrder(ElectronicOrderModel order)
        {
            order.TradeStatus = TradeStatus.PAYERROR;
            _logger.Info("UnifiedOrder is processing order " + order.BillNumber);
            var config = (AliPayConfig)order.Config;

            AlipayTradePrecreateContentBuilder builder = BuildPrecreateContent(order);
            AlipayF2FPrecreateResult payResult = await Clients(config).tradePrecreate(builder);

            switch (payResult.Status)
            {
                case ResultEnum.SUCCESS:
                    order.TradeStatus = TradeStatus.SUCCESS;
                    break;
                case ResultEnum.FAILED:
                    _logger.Info("Request failed with result: ResultEnum.FAILED");
                    break;
                case ResultEnum.UNKNOWN:
                    _logger.Info("Failed due to unknown reason, plesae check your network configuration and cable connection, " +
                        "then retry with a new order number");
                    break;
            }

            _logger.Info("The final Alipay response is :\r\n" + HttpUtility.UrlDecode(payResult.response?.Body));
            return new GenericProcessResponse()
            {
                AopResponse = payResult.response
            };
        }
        public override async Task<GenericProcessResponse> Return(ElectronicOrderModel order)
        {
            _logger.Info("Alipay is processing refund, BillNumber = " + order.BillNumber);

            AlipayTradeRefundContentBuilder builder = BuildRefundContent(order);
            order.TradeStatus = TradeStatus.PAYERROR;
            AlipayF2FRefundResult refundResult = await Clients((AliPayConfig)order.Config).tradeRefund(builder);

            //请在这里加上商户的业务逻辑程序代码
            switch (refundResult.Status)
            {
                case ResultEnum.SUCCESS:
                    order.TradeStatus = TradeStatus.SUCCESS;
                    break;
                case ResultEnum.FAILED:
                    _logger.Info("Request failed with result: ResultEnum.FAILED");
                    break;
                case ResultEnum.UNKNOWN:
                    if (refundResult.response == null)
                    {
                        _logger.Info("Failed due to unknown reason, plesae check your network configuration and cable connection, " +
                            "then retry with a new order number");
                    }
                    else
                    {
                        _logger.Info("System error, please try refunding mannualy");
                    }
                    break;
            }

            _logger.Info("The final Alipay refund response is :\r\n" + refundResult.response?.Body);
            return new GenericProcessResponse()
            {
                AopResponse = refundResult.response
            };
        }
        public override async Task<GenericProcessResponse> Query(ElectronicOrderModel order)
        {
            return await Query(order, 1, 2000);
        }

        public override async Task<GenericProcessResponse> Query(ElectronicOrderModel order, int count, int interval)
        {
            order.TradeStatus = TradeStatus.PAYERROR;
            _logger.Info("Ali Query is processing order: " + order.BillNumber);
            //商户订单号
            string out_trade_no = order.BillNumber;

            //商户网站订单系统中唯一订单号，必填
            var queryResult = await Clients((AliPayConfig)order.Config).tradeLoopQuery(out_trade_no, count, interval);

            if (queryResult.TradeStatus == "TRADE_FINISHED" ||
                queryResult.TradeStatus == "TRADE_SUCCESS")
            {
                order.TradeStatus = TradeStatus.SUCCESS;
            }
            else
            {
                _logger.Info("Failed due to unknown reason, plesae check your network configuration and cable connection, " +
                    "then retry with a new order number");
            }


            _logger.Info("Ali Query process complete, result : \r\n" + queryResult != null ? queryResult.Body : "");

            return new GenericProcessResponse()
            {
                AopResponse = queryResult
            };
        }

        public override async Task<GenericProcessResponse> Cancel(ElectronicOrderModel cancelOrder)
        {
            _logger.Info("Ali CodePay failure, reverse order " + cancelOrder.BillNumber);
            return new GenericProcessResponse();
        }

        /// <summary>
        /// 构造退款请求数据
        /// </summary>
        /// <returns>请求数据集</returns>
        private AlipayTradeRefundContentBuilder BuildRefundContent(ElectronicOrderModel order)
        {
            AlipayTradeRefundContentBuilder builder = new AlipayTradeRefundContentBuilder();

            //支付宝交易号与商户网站订单号不能同时为空
            builder.out_trade_no = order.BillNumber;

            //退款请求单号保持唯一性。
            builder.out_request_no = order.BillNumber + DateTime.Now.ToString();

            //退款金额
            builder.refund_amount = order.NetAmount.ToString();

            builder.refund_reason = "refund from POS";

            return builder;

        }
        /// <summary>
        /// 构造支付请求数据
        /// </summary>
        /// <returns>请求数据集</returns>
        private AlipayTradePrecreateContentBuilder BuildPrecreateContent(ElectronicOrderModel order)
        {
            //线上联调时，请输入真实的外部订单号。
            string out_trade_no = order.BillNumber;

            var config = (AliPayConfig)order.Config;

            //扫码枪扫描到的用户手机钱包中的付款条码
            var builder = new AlipayTradePrecreateContentBuilder();

            //收款账号
            builder.seller_id = config.pid;
            //订单编号
            builder.out_trade_no = out_trade_no;
            //订单总金额
            builder.total_amount = order.NetAmount.ToString();
            //参与优惠计算的金额
            //builder.discountable_amount = "";
            //不参与优惠计算的金额
            //builder.undiscountable_amount = "";
            //订单名称
            builder.subject = order.Title;
            //自定义超时时间
            builder.timeout_express = "30m";
            //订单描述
            builder.body = "";
            //门店编号，很重要的参数，可以用作之后的营销
            builder.store_id = order.SiteId;
            //操作员编号，很重要的参数，可以用作之后的营销
            builder.operator_id = order.OperatorId;


            //传入商品信息详情
            List<GoodsInfo> gList = new List<GoodsInfo>();
            order.FuelOrderDetails.ForEach(i =>
            {
                GoodsInfo goods = new GoodsInfo();
                goods.goods_id = i.FuelProductId;
                goods.goods_name = i.FuelProductName;
                goods.price = i.Amount.ToString();
                goods.quantity = i.Qualtity.ToString();
                gList.Add(goods);
            });

            builder.goods_detail = gList;

            //系统商接入可以填此参数用作返佣
            //ExtendParams exParam = new ExtendParams();
            //exParam.sysServiceProviderId = "20880000000000";
            //builder.extendParams = exParam;

            return builder;

        }

    }
}
