package com.hzs.third.pay.service.impl;

import cn.hutool.json.JSONUtil;
import com.hzs.common.core.domain.R;
import com.hzs.common.domain.third.pay.TOnlineCard;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.third.pay.config.AllInPayConfig;
import com.hzs.third.pay.constants.AllInPayConstants;
import com.hzs.third.pay.constants.PayRedisConstants;
import com.hzs.third.pay.dto.WechatStateDTO;
import com.hzs.third.pay.service.IAllInPayService;
import com.hzs.third.pay.service.ITOnlineCardService;
import com.hzs.third.pay.util.AllInPayUtil;
import com.hzs.third.pay.util.HttpConnectionUtil;
import com.hzs.third.pay.util.PayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

/**
 * 通联支付服务
 */
@Slf4j
@Service
public class AllInPayServiceImpl implements IAllInPayService {

    @Autowired
    private AllInPayConfig allInPayConfig;
    @Autowired
    private ITOnlineCardService itOnlineCardService;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public R<String> createCode(TOnlinePayment onlinePayment) {
        try {
            if (onlinePayment.getPayMoney().compareTo(new BigDecimal("20000")) >= 0) {
                return R.fail("当前支付方式单笔金额不能超过20000");
            }
            // 创建缓存对象
            this.createCache(onlinePayment);

            log.info("通联小程序支付订单：{}", onlinePayment.getBusinessCode());

            return R.ok(onlinePayment.getBusinessCode());
        } catch (Exception e) {
            log.error("通联小程序支付返回异常", e);
        }
        return R.fail("通联小程序支付返回失败");
    }

    /**
     * 封装订单缓存
     *
     * @param onlinePayment 支付信息
     */
    private void createCache(TOnlinePayment onlinePayment) {
        // 缓存对象
        WechatStateDTO wechatStateDTO = new WechatStateDTO();
        wechatStateDTO.setType(onlinePayment.getBusinessType());
        wechatStateDTO.setOrderCode(onlinePayment.getBusinessCode());
        wechatStateDTO.setPkMember(onlinePayment.getPkModified());
        // redis 缓存KEY
        String stateKey = String.format(PayRedisConstants.ALLIN_STATE_KEY, onlinePayment.getBusinessCode());
        // 放到redis中缓存（有效期24小时）
        redisTemplate.opsForValue().set(stateKey, JSONUtil.toJsonStr(wechatStateDTO), 24, TimeUnit.HOURS);
    }

    @Override
    public R<String> bankPay(TOnlinePayment onlinePayment, String bindCode) {
        try {
            // 查询绑卡数据
            TOnlineCard tOnlineCard = itOnlineCardService.queryCardByCode(bindCode, null, allInPayConfig.getCustomerNum(), null);
            if (null == tOnlineCard) {
                return R.fail("银行卡未进行绑定");
            }

            // 与三方对接，需要扩展支付订单号，用于标记业务
            String thirdOrderCode = PayUtil.getThirdOrderCode(onlinePayment.getBusinessCode(), onlinePayment.getBusinessType());
            onlinePayment.setOriginalOrder(thirdOrderCode);

            TreeMap<String, String> params = new TreeMap<>();
            params.put("appid", allInPayConfig.getAppId());
            params.put("cusid", allInPayConfig.getCustomerNum());
            params.put("randomstr", System.currentTimeMillis() + "");
            params.put("signtype", AllInPayConstants.SIGN_TYPE);
            params.put("version", AllInPayConstants.VERSION);

            params.put("reqsn", thirdOrderCode);
            params.put("amount", onlinePayment.getPayMoney().multiply(new BigDecimal("100")).intValue() + "");
            params.put("subject", "支付：" + onlinePayment.getBusinessCode());
            params.put("trxreserve", onlinePayment.getBusinessType().toString());
            params.put("notifyurl", allInPayConfig.getCallbackUrl());
            params.put("currency", "CNY");
            params.put("agreeid", tOnlineCard.getBindId());

            log.info("通联银行卡下单请求参数： {}", params);

            Map<String, String> resultMap = this.doRequest(allInPayConfig.getRequestUrl() + "/qpay/payapplyagree", params);

            log.info("通联银行卡下单返回数据： {}", resultMap);

            if (AllInPayConstants.SUCCESS_CODE.equals(resultMap.get("trxstatus"))
                    || AllInPayConstants.SMS_SUCCESS.equals(resultMap.get("trxstatus"))) {
                // 返回成功
                return R.ok();
            }
        } catch (Exception e) {
            log.error("通联银行卡下单异常!", e);
        }
        return R.fail("通联银行卡下单返回失败");
    }

    /**
     * 发送请求
     *
     * @param url
     * @param params
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, String> doRequest(String url, TreeMap<String, String> params) throws Exception {
        params.put("sign", AllInPayUtil.unionSign(params, allInPayConfig.getPrivateKey(), AllInPayConstants.SIGN_TYPE));
        HttpConnectionUtil http = new HttpConnectionUtil(url);
        http.init();
        byte[] bys = http.postParams(params, true);
        String result = new String(bys, StandardCharsets.UTF_8);
        return this.handleResult(result);
    }

    /**
     * 统一结果处理
     *
     * @param result
     * @return
     * @throws Exception
     */
    private Map<String, String> handleResult(String result) throws Exception {
        log.info("通联支付返回数据 ret: {}", result);
        Map map = JSONUtil.toBean(result, Map.class);
        if (map == null) {
            throw new Exception("返回数据错误");
        }
        if (AllInPayConstants.SUCCESS.equals(map.get("retcode"))) {
            if (AllInPayUtil.validSign(new TreeMap(map), allInPayConfig.getPublicKey(), AllInPayConstants.SIGN_TYPE)) {
                log.info("签名成功");
                return map;
            } else {
                throw new Exception("验证签名失败");
            }
        } else {
            throw new Exception(map.get("retmsg").toString());
        }
    }

}
