using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Aop.Api.Request;
using Aop.Api.Response;
using BaseModel.Models;
using WayneCloud.PaymentProcessors.Alipay.FromSDK;

namespace Aop.Api.Business
{
    public class CodePay
    {
        private static volatile IAopClient client;

        private static volatile Dictionary<AliPayConfig, IAopClient> clients = new Dictionary<AliPayConfig, IAopClient>();
        public static IAopClient Clients(AliPayConfig config)
        {
            //if (config != null)
            //{
                //if (client == null)
                //{
                //    client = new DefaultAopClient(config.serverUrl,
                //                                         config.appId,
                //                                         config.merchant_private_key,
                //                                         "json",
                //                                         config.version,
                //                                         config.sign_type,
                //                                         config.alipay_public_key,
                //                                         config.charset);
                //}
                //return client;

                if (!clients.ContainsKey(config))
                {
                    var newClient = new DefaultAopClient(config.serverUrl,
                                                                     config.appId,
                                                                     config.merchant_private_key,
                                                                     "json",
                                                                     config.version,
                                                                     config.sign_type,
                                                                     config.alipay_public_key,
                                                                     config.charset);
                    clients.Add(config, newClient);
                    return newClient;
                }
                else
                {
                    return clients[config];
                }
        }

        public static async Task<AopResponse> Run(ElectronicOrderModel order)
        {
            //set the default trade_status to PAYERROR
            order.TradeStatus = TradeStatus.PAYERROR;

            //线上联调时，请输入真实的外部订单号。
            string out_trade_no = order.BillNumber;

            //线上联调时，请输入真实的条码。
            string auth_code = order.AuthCode; //扫码枪扫描到的用户手机钱包中的付款条码
            string total_amount = order.NetAmount.ToString();

            StringBuilder sb = new StringBuilder();

            sb.Append("{\"out_trade_no\":\"" + out_trade_no + "\",");
            sb.Append("\"scene\":\"bar_code\",");
            sb.Append("\"auth_code\":\"" + auth_code + "\",");
            sb.Append("\"total_amount\":\"" + total_amount + "\",\"discountable_amount\":\"0.00\",");
            sb.Append("\"subject\":\"" + order.Title + "\",\"body\":\"empty_body\",");
            sb.Append("\"goods_detail\":[");
            var goodsString = (order.FuelOrderDetails.Count == 0) ? "" : 
                (order.FuelOrderDetails.Select(s => "{\"goods_id\":\"" + s.FuelProductId
                + "\",\"goods_name\":\"" + s.FuelProductName
                + "\",\"goods_category\":\"" + s.Category
                + "\",\"price\":\"" + s.Price
                + "\",\"quantity\":\"" + s.Qualtity + "\"}").Aggregate((acc, n) => acc + "," + n));
            sb.Append(goodsString);
            sb.Append("],");
            sb.Append("\"operator_id\":\"" + (order.OperatorId ?? "") + "\",\"store_id\":\"" + order.SiteId + "\",\"terminal_id\":\"" + (order.TerminalId ?? "") + "\",");

            string expire_time = System.DateTime.Now.AddHours(1).ToString("yyyy-MM-dd HH:mm:ss");
            sb.Append("\"time_expire\":\"" + expire_time + "\"}");

            var payResponse = await Pay(sb.ToString(), (AliPayConfig)order.Config);

            //支付成功的情形
            if (payResponse != null && payResponse.Code == ResultCode.SUCCESS)
            {
                order.TradeStatus = TradeStatus.SUCCESS;
                return payResponse;
            }
            
            //如果返回结果是操作失败，或者返回结果不是操作处理中，就直接返回支付失败
            if (payResponse == null || payResponse.Code == ResultCode.FAIL || payResponse.Code != ResultCode.INRROCESS)
            {
                return payResponse;
            }

            StringBuilder sb1 = new StringBuilder();
            sb1.Append("{\"out_trade_no\":\"" + order.BillNumber + "\"}");

            //以下是返回操作处理中的情形, 需要进行轮询
            AlipayTradeQueryResponse queryResponse = await LoopQuery(sb1.ToString(), (AliPayConfig)order.Config);   //用订单号trade_no进行轮询也是可以的。
            if (queryResponse !=null)
            {
                if (string.Compare(queryResponse.Code, ResultCode.SUCCESS, false) == 0)
                {
                    if (queryResponse.TradeStatus == "TRADE_FINISHED"
                        || queryResponse.TradeStatus == "TRADE_SUCCESS")
                    {
                        order.TradeStatus = TradeStatus.SUCCESS;
                    }
                    else if (queryResponse.TradeStatus == "TRADE_CLOSED")
                    {
                        order.TradeStatus = TradeStatus.CLOSED;
                    }
                    return queryResponse;
                }
            }

            return payResponse;
        }

        private static async Task<AlipayTradePayResponse> Pay(string biz_content, AliPayConfig config)
        {
            AlipayTradePayRequest payRequst = new AlipayTradePayRequest();
            payRequst.BizContent = biz_content;
            //payRequst
            Dictionary<string, string> paramsDict = (Dictionary<string, string>)payRequst.GetParameters();
            //new DefaultAopClient(Config.serverUrl, Config.appId, Config.merchant_private_key);

            AlipayTradePayResponse payResponse = await Clients(config).Execute(payRequst);
            return payResponse;
        }

        public static async Task<AlipayTradeCancelResponse> Cancel(string billNumber, AliPayConfig config)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("{\"out_trade_no\":\"" + billNumber + "\"}");

            string biz_content = sb.ToString();
            AlipayTradeCancelRequest cancelRequest = new AlipayTradeCancelRequest();
            cancelRequest.BizContent = biz_content;
            AlipayTradeCancelResponse cancelResponse = await Clients(config).Execute(cancelRequest);

            if (null != cancelResponse)
            {
                if (cancelResponse.Code == ResultCode.FAIL && cancelResponse.RetryFlag == "Y")
                {
                   cancelResponse = await CancelOrderRetry(biz_content, config);
                }
            }
            return cancelResponse;
        }

        private static async Task<AlipayTradeCancelResponse> CancelOrderRetry(object o, AliPayConfig config)
        {
            int retryCount = 10;

            AlipayTradeCancelResponse cancelResponse = null;
            for (int i = 0; i < retryCount; ++i)
            {

                Thread.Sleep(5000);
                AlipayTradeCancelRequest cancelRequest = new AlipayTradeCancelRequest();
                cancelRequest.BizContent = o.ToString();
                // Dictionary<string, string> paramsDict = (Dictionary<string, string>)cancelRequest.GetParameters();
                cancelResponse = await Clients(config).Execute(cancelRequest);

                if (null != cancelResponse)
                {
                    if (cancelResponse.Code == ResultCode.FAIL)
                    {
                        //if (cancelResponse.Body.Contains("\"retry_flag\":\"N\""))		
                        if (cancelResponse.RetryFlag == "N")
                        {
                            break;
                        }
                    }
                }

                if (i == retryCount - 1)
                {
                    // 处理到最后一次，还是未撤销成功，需要在商户数据库中对此单最标记，人工介入处理
                }

            }
            return cancelResponse;
        }

        private static async Task<AlipayTradeQueryResponse> LoopQuery(string biz_content, AliPayConfig config)
        {

            AlipayTradeQueryRequest payRequst = new AlipayTradeQueryRequest();
            payRequst.BizContent = biz_content;

            Dictionary<string, string> paramsDict = (Dictionary<string, string>)payRequst.GetParameters();
            AlipayTradeQueryResponse payResponse = null;

            for (int i = 1; i <= 6; i++)
            {
                Thread.Sleep(5000);
                payResponse = await Clients(config).Execute(payRequst);
                if (string.Compare(payResponse.Code, ResultCode.SUCCESS, false) == 0)
                {
                    if (payResponse.TradeStatus == "TRADE_FINISHED"
                        || payResponse.TradeStatus == "TRADE_SUCCESS"
                        || payResponse.TradeStatus == "TRADE_CLOSED")
                        return payResponse;
                }

            }
            return payResponse;
        }
    }
}
