package com.liquidnet.service.dragon.channel.wepay.strategy.impl;

import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.dragon.biz.DragonServiceCommonBiz;
import com.liquidnet.service.dragon.channel.strategy.biz.DragonPayBiz;
import com.liquidnet.service.dragon.channel.wepay.biz.WepayBiz;
import com.liquidnet.service.dragon.channel.wepay.constant.WepayConstant;
import com.liquidnet.service.dragon.channel.wepay.resp.WepayPayRespDto;
import com.liquidnet.service.dragon.channel.wepay.strategy.IWepayStrategy;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.constant.DragonErrorCodeEnum;
import com.liquidnet.service.dragon.dto.DragonOrdersDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseReqDto;
import com.liquidnet.service.dragon.dto.DragonPayBaseRespDto;
import com.liquidnet.service.dragon.dto.DragonPayOrderQueryRespDto;
import com.liquidnet.service.dragon.utils.DataUtilsDragon;
import com.liquidnet.service.dragon.config.WepayAppletPayConfigure;
import com.liquidnet.service.dragon.support.WepayAppletPaySupport;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import com.liquidnet.service.dragon.utils.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author AnJiabin <anjiabin@zhengzai.tv>
 * @version V1.0
 * @Description: TODO
 * @class: AbstractWepayStrategy
 * @Package com.liquidnet.service.dragon.channel.wepay.strategy.impl
 * @Copyright: LightNet @ Copyright (c) 2021
 * @date 2021/7/11 11:56
 */
@Slf4j
public abstract class AbstractWepayStrategy implements IWepayStrategy {
    @Autowired
    private WepayBiz wepayBiz;

    @Autowired
    private DataUtilsDragon dataUtilsDragon;

    @Autowired
    private DragonServiceCommonBiz dragonServiceCommonBiz;

    @Autowired
    private DragonPayBiz dragonPayBiz;

    @Override
    public ResponseDto<DragonPayBaseRespDto> dragonPay(DragonPayBaseReqDto dragonPayBaseReqDto) {
        long startTimeTotal = System.currentTimeMillis();
        long startTime = System.currentTimeMillis();
        try {
            //构造请求参数
            SortedMap<String, Object> commonParams = this.buildRequestParamMap(dragonPayBaseReqDto);
            //追加请求参数
            SortedMap<String, Object> parameters = this.appendRequestParam(commonParams,dragonPayBaseReqDto);
            //生成签名
            String sign = PayWepayUtils.getInstance().createSign(parameters);
            parameters.put("sign", sign);

            //构造支付请求xml
            String data = PayWepayUtils.getInstance().getRequestXml(parameters);

            log.info("dragonPay:wepay:"+dragonPayBaseReqDto.getDeviceFrom()+" request jsondata: {} ",data);


            HttpPost httpost = new HttpPost(this.getRequestUrl());
            httpost.setEntity(new StringEntity(data, "UTF-8"));

            startTime = System.currentTimeMillis();
            CloseableHttpClient httpClient = PayWepayUtils.getInstance().getHttpClient();
            log.info("wepay-->request--> getHttpClient耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");

            startTime = System.currentTimeMillis();
            CloseableHttpResponse response = httpClient.execute(httpost);
            log.info("wepay-->request--> execute耗时:{}",(System.currentTimeMillis() - startTime)+"毫秒");

            HttpEntity entity = response.getEntity();
            //接受到返回信息
            String xmlStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            EntityUtils.consume(entity);
            log.info("dragonPay:wepay:"+dragonPayBaseReqDto.getDeviceFrom()+" response jsonStr: {} ",xmlStr);

            WepayPayRespDto respWepayDto= null;
            try {
                respWepayDto = XmlUtil.toBean(xmlStr, WepayPayRespDto.class);
            } catch (Exception e) {
                log.error("dragonPay:wepay:"+dragonPayBaseReqDto.getDeviceFrom()+" response format error: {} ",e);
                throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
            }
            if(WepayConstant.WeixinTradeStateEnum.SUCCESS.getCode().equalsIgnoreCase(respWepayDto.getReturnCode())){
                if(WepayConstant.WeixinTradeStateEnum.SUCCESS.getCode().equalsIgnoreCase(respWepayDto.getResultCode())||dragonPayBaseReqDto.getDeviceFrom().equals(DragonConstant.DeviceFromEnum.MICROPAY.getCode())){
                    //构造公共返回参数
                    DragonPayBaseRespDto respPayDto = this.buildCommonRespDto(dragonPayBaseReqDto,respWepayDto);
                    //构造自定义返回参数
                    this.buildResponseDto(respPayDto,respWepayDto);
                    if(!WepayConstant.WeixinTradeStateEnum.SUCCESS.getCode().equalsIgnoreCase(respWepayDto.getResultCode())&&dragonPayBaseReqDto.getDeviceFrom().equals(DragonConstant.DeviceFromEnum.MICROPAY.getCode())) {
                        respPayDto.setMsg(respWepayDto.getErr_code_des());
                    }
                    //支付订单持久化
                    dragonServiceCommonBiz.buildPayOrders(dragonPayBaseReqDto,respPayDto);
                    log.info("wepay-->dragonPay--> 耗时:{}",(System.currentTimeMillis() - startTimeTotal)+"毫秒");
                    return ResponseDto.success(respPayDto);
                }else{
                    throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
                }
            }else{
                throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_PARAM_ERROR.getCode(),DragonErrorCodeEnum.TRADE_PARAM_ERROR.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 构造公共返回参数
     * @param dragonPayBaseReqDto
     * @return
     */
    protected DragonPayBaseRespDto buildCommonRespDto(DragonPayBaseReqDto dragonPayBaseReqDto,WepayPayRespDto respWepayDto){
        String nonceStr = PayWepayUtils.getInstance().getNonceStr();
        DragonPayBaseRespDto respDto = new DragonPayBaseRespDto();
        respDto.setCode(dragonPayBaseReqDto.getCode());
        respDto.setOrderCode(dragonPayBaseReqDto.getOrderCode());
        respDto.setPayType(dragonPayBaseReqDto.getPayType());
        DragonPayBaseRespDto.PayData payData = new DragonPayBaseRespDto.PayData();
        payData.setAppId(respWepayDto.getAppid());
        payData.setNonceStr(nonceStr);
        payData.setPackages("prepay_id="+respWepayDto.getPrepayId());
        payData.setSignType("MD5");
        Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        payData.setTimeStamp(second+"");
        payData.setPartnerId(respWepayDto.getMchId());
        payData.setPrepayId(respWepayDto.getPrepayId());
        respDto.setPayData(payData);
        return respDto;
    }

    /**
     * 构造请求参数
     * @return
     */
    protected SortedMap<String, Object> buildRequestParamMap(DragonPayBaseReqDto dragonPayBaseReqDto){
        String nonceStr = PayWepayUtils.getInstance().getNonceStr();
        SortedMap<String, Object> parameters = new TreeMap<>();
//        parameters.put("mch_id", PayWepayUtils.getInstance().getMerchantId());
        if (WepayAppletPaySupport.usesMerchantB(dragonPayBaseReqDto.getAppIdType())) {
            parameters.put("mch_id", PayWepayUtils.getInstance().getMerchantBId());
        } else {
            parameters.put("mch_id", PayWepayUtils.getInstance().getMerchantId());
        }
        parameters.put("nonce_str", nonceStr);
        parameters.put("spbill_create_ip", dragonPayBaseReqDto.getClientIp());
        parameters.put("total_fee", dragonPayBaseReqDto.getPrice().multiply(BigDecimal.valueOf(100L)).intValue()+"");
        parameters.put("body", dragonPayBaseReqDto.getName());
        parameters.put("detail", dragonPayBaseReqDto.getDetail());
        parameters.put("out_trade_no", dragonPayBaseReqDto.getCode());
        String timeExpire = DateUtil.format(DateUtil.Formatter.yyyyMMddHHmmss.parse(dragonPayBaseReqDto.getCreateDate()).plusMinutes(Long.parseLong(dragonPayBaseReqDto.getExpireTime())),DateUtil.Formatter.yyyyMMddHHmmssTrim);
        parameters.put("time_expire", timeExpire);
        parameters.put("notify_url", this.getNotifyUrl());
        return parameters;
    };

    /**
     * 追加请求参数
     * @param requestMap
     * @return
     */
    abstract SortedMap<String, Object> appendRequestParam(SortedMap<String, Object> requestMap,DragonPayBaseReqDto dragonPayBaseReqDto);

    /**
     * 构造返回参数
     */
    abstract DragonPayBaseRespDto buildResponseDto(DragonPayBaseRespDto payBaseRespDto,WepayPayRespDto respDto);

    /**
     * 获取请求url
     * @return
     */
    protected abstract String getRequestUrl();

    /**
     * 设置notifyUrl
     */
    protected abstract String getNotifyUrl();

    @Override
    public DragonPayOrderQueryRespDto checkOrderStatus(String code) {
        DragonOrdersDto ordersDto = dataUtilsDragon.getPayOrderByCode(code);
        Map<String, Object> resultMap = null;
        String queryAppid = WepayAppletPayConfigure.resolveAppletAppIdForTradeQuery(ordersDto.getPaymentType(), code);
        resultMap = wepayBiz.tradeQuery(code, queryAppid);
        DragonPayOrderQueryRespDto respDto = dragonPayBiz.buildPayOrderQueryRespDto(ordersDto);



        Object returnCode = resultMap.get("return_code");
        // 查询失败
        if (null == returnCode || "FAIL".equals(returnCode)) {
            throw new LiquidnetServiceException(DragonErrorCodeEnum.TRADE_WEPAY_QUERY_ERROR.getCode(),DragonErrorCodeEnum.TRADE_WEPAY_QUERY_ERROR.getMessage());
        }
        // 当trade_state为SUCCESS时才返回result_code
        if ("SUCCESS".equals(resultMap.get("trade_state"))) {
            respDto.setStatus(Integer.valueOf(DragonConstant.PayStatusEnum.STATUS_PAID.getCode()));
        }
        return respDto;
    }

    protected abstract String getAppid();
}
