package com.hzs.third.pay.controller.api;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hzs.common.core.enums.EPayBusinessType;
import com.hzs.common.core.enums.EPayChannel;
import com.hzs.common.core.enums.EPayStatus;
import com.hzs.common.core.text.CharsetKit;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.core.utils.StringUtils;
import com.hzs.common.core.web.domain.AjaxResult;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.third.pay.config.BaoFuPayConfig;
import com.hzs.third.pay.config.WeChatConfig;
import com.hzs.third.pay.constants.BaoFuPayConstants;
import com.hzs.third.pay.constants.PayRedisConstants;
import com.hzs.third.pay.controller.base.PayBaseController;
import com.hzs.third.pay.dto.WechatPublicDTO;
import com.hzs.third.pay.dto.WechatStateDTO;
import com.hzs.third.pay.param.WechatCodeParam;
import com.hzs.third.pay.service.ITOnlinePaymentService;
import com.hzs.third.pay.util.PayUtil;
import com.hzs.third.pay.util.WeChatUtil;
import com.hzs.third.pay.util.baofu.SignatureUtils;
import com.hzs.third.wechat.dto.WeChatCodeDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 宝付二维码支付控制器
 */
@Slf4j
@RestController
@RequestMapping("/baofu-code")
public class BaoFuPayCodeController extends PayBaseController {

    @Autowired
    private WeChatConfig weChatConfig;
    @Autowired
    private BaoFuPayConfig baoFuPayConfig;
    @Autowired
    private ITOnlinePaymentService itOnlinePaymentService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 宝付下单（移动端）
     *
     * @param param
     * @return
     */
    @PostMapping("/order")
    public AjaxResult order(@RequestBody WechatCodeParam param) {
        if (StringUtils.isAnyEmpty(param.getCode(), param.getState())) {
            return AjaxResult.error("缺少参数");
        }

        try {
            // 微信请求参数
            Map<String, Object> requestParam = new HashMap<>();
            requestParam.put("appid", weChatConfig.getPublicAppId());
            requestParam.put("secret", weChatConfig.getPublicAppSecret());
            requestParam.put("code", param.getCode());
            requestParam.put("grant_type", "authorization_code");

            // 根据code调用微信 oauth2.access_token 接口，获取用户 openId
            String resultStr = HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/access_token", requestParam);
            if (StringUtils.isEmpty(resultStr)) {
                return AjaxResult.error("请求微信授权异常");
            }
            // 微信返回json转操作对象
            WechatPublicDTO wechatPublicDTO = JSONUtil.parseObj(resultStr).toBean(WechatPublicDTO.class);

            // 判断返回内容是否成功
            String errorMsg = WeChatUtil.handErrorCode(wechatPublicDTO.getErrcode());
            if (StringUtils.isNotEmpty(errorMsg)) {
                return AjaxResult.error(errorMsg);
            }
            // 用户公众号授权的 openId
            String openId = wechatPublicDTO.getOpenid();
            log.info("调用宝付前置微信授权! order: {}, openId: {}", param.getState(), openId);
            // 测试使用
//            String openId = "ohLJU6P9Ivzq_6KeMU37yc6pyOxg";

            // redis 缓存KEY
            String stateKey = String.format(PayRedisConstants.BAOFU_STATE_KEY, param.getState());
            if (!redisTemplate.hasKey(stateKey)) {
                log.error("宝付微信支付失败，缓存订单已不存在");
                return AjaxResult.error("支付失败，请重新发起支付");
            } else {
                Object payInfo = redisTemplate.opsForValue().get(stateKey);
                if (null == payInfo) {
                    log.error("宝付微信支付失败，缓存订单为空");
                    return AjaxResult.error("支付失败，请重新发起支付");
                }
                // 缓存的支付信息
                WechatStateDTO wechatStateDTO = JSONUtil.toBean(payInfo.toString(), WechatStateDTO.class);

                // 支付类型
                int businessType = wechatStateDTO.getType();
                // 订单编号
                String orderCode = wechatStateDTO.getOrderCode();
                // 三方订单编号（因调起微信方式没有扩展参数，需要自处理业务类型），宝付订单编号不允许-号，所以此处使用_
                String thirdOrderCode = orderCode + "_" + businessType + RandomUtil.randomNumbers(4);

                TOnlinePayment tOnlinePayment = itOnlinePaymentService.queryByBusiness(businessType, orderCode, null);
                if (null == tOnlinePayment) {
                    log.error("宝付微信支付失败，在线支付信息不存在");
                    return AjaxResult.error("支付失败，请重新发起支付");
                }
                if (EPayStatus.PAID.getValue() == tOnlinePayment.getPayStatus()) {
                    // 在线支付信息已支付
                    log.error("宝付微信支付失败，在线支付信息已支付");
                    return AjaxResult.error("订单已支付");
                }

                // 实际支付金额（随机立减之后）
                BigDecimal amount = tOnlinePayment.getBusinessMoney();

                // 部分业务金额随机立减
                if (EPayBusinessType.getEnumByValue(businessType) == EPayBusinessType.RECHARGE
                        || EPayBusinessType.getEnumByValue(businessType) == EPayBusinessType.DIRECT_SELLING) {
                    // 充值、直销订单
                    amount = PayUtil.handleAmountRandom(tOnlinePayment.getBusinessMoney());
                }
                // 请求时间戳
                String timeStr = DateUtils.parseDateTimeToStr(DateUtils.YAMMERERS, new Date());

                // 接口请求数据
                Map<String, Object> bizContent = new HashMap<>();
                bizContent.put("merId", baoFuPayConfig.getMerId());
                bizContent.put("terId", baoFuPayConfig.getTerId());

                // 订单编号
                bizContent.put("outTradeNo", thirdOrderCode);
                // 交易时间（年月日时分秒）
                bizContent.put("txnTime", timeStr);
                // 实际付款金额（分）
                bizContent.put("txnAmt", amount.multiply(new BigDecimal("100")).intValue());
                // 订单总金额（分）
                bizContent.put("totalAmt", amount.multiply(new BigDecimal("100")).intValue());
                // 订单超时时间（24小时）
                bizContent.put("timeExpire", "1440");
                // 普通产品（ORDINARY），发账产品（分账产品）
                bizContent.put("prodType", "ORDINARY");
                // 支付方式（微信JSAPI： WECHAT_JSAPI， 支付宝扫码支付：ALIPAY_NATIVE）
                bizContent.put("payCode", BaoFuPayConstants.WECHAT_JSAPI);
                // 支付属性
                Map<String, Object> payExtend = new HashMap<>();
                payExtend.put("body", "支付：" + orderCode);
                // 扫码方式，公众号appId
                payExtend.put("sub_appid", weChatConfig.getPublicAppId());
                payExtend.put("sub_openid", openId);
                bizContent.put("payExtend", payExtend);
                // 风控参数
                Map<String, String> riskMap = new HashMap<>();
                riskMap.put("riskInfo", InetAddress.getLocalHost().getHostAddress());
                bizContent.put("riskInfo", riskMap);
                // 二级商户号（测试传空，生产传SIMID）
                bizContent.put("subMchId", baoFuPayConfig.getSubMchId());
                // 支付成功回调地址
                bizContent.put("notifyUrl", baoFuPayConfig.getCallbackUrl());
                // 支付成功跳转页面
                bizContent.put("pageUrl", baoFuPayConfig.getFrontUrl());

                // 请求数据转json串
                String bizContentStr = JSONUtil.toJsonStr(bizContent);
                // 请求数据生成请求签名
                String signStr = SignatureUtils.encryptByRSA(bizContentStr, baoFuPayConfig.getPfxPath(), baoFuPayConfig.getPfxPwd());

                // 统一请求内容
                Map<String, Object> postMap = new HashMap<>();
                postMap.put("merId", baoFuPayConfig.getMerId());
                postMap.put("terId", baoFuPayConfig.getTerId());
                postMap.put("method", "unified_order");
                postMap.put("charset", CharsetKit.UTF_8);
                postMap.put("version", "1.0");
                postMap.put("format", "json");
                postMap.put("timestamp", timeStr);
                // 加密签名类型
                postMap.put("signType", "RSA");
                // 签名证书序列号，默认1
                postMap.put("signSn", "1");
                // 加密证书序列号，默认1
                postMap.put("ncrptnSn", "1");
                // 发送方私钥签名的非对称密钥，并做16进制转码
                postMap.put("signStr", signStr);
                // 业务数据（json字符串）
                postMap.put("bizContent", bizContentStr);

                log.info("宝付支付请求参数：{}", postMap);

                // 请求接口返回内容
                String resultPayStr = HttpUtil.post(baoFuPayConfig.getRequestUrl(), postMap);

                log.info("宝付支付返回参数：{}", resultPayStr);

                Map<String, String> resultMap = JSONUtil.toBean(resultPayStr, HashMap.class);

                if (BaoFuPayConstants.SUCCESS.equals(resultMap.get("returnCode"))) {
                    // 返回成功
                    if (SignatureUtils.verifySignature(baoFuPayConfig.getCerPath(), resultMap.get("dataContent"), resultMap.get("signStr"))) {
                        // 验签成功

                        // 返回的业务数据
                        Map<String, Object> dataMap = JSONUtil.toBean(resultMap.get("dataContent"), HashMap.class);

                        // 订单参数
                        String txnState = dataMap.get("txnState").toString();
                        if (BaoFuPayConstants.STATE_WAIT_PAYING.equals(txnState)) {
                            // 下单成功，处理返回参数
                            // 渠道返回参数
                            Map<String, String> chlRetParamMap = JSONUtil.toBean(dataMap.get("chlRetParam").toString(), HashMap.class);
                            // 前端使用支付参数
                            Map<String, String> payDataMap = JSONUtil.toBean(chlRetParamMap.get("wc_pay_data"), HashMap.class);

                            // 微信支付数据
                            return AjaxResult.success(payDataMap);
                        } else if (BaoFuPayConstants.SUCCESS.equals(txnState)) {
                            // 订单已交易成功
                            return AjaxResult.error("订单已交易成功，不能重付支付");
                        } else {
                            // 其它情况，订单状态异常
                            return AjaxResult.error("订单异常，请刷新后重试");
                        }
                    }
                    log.error("调用宝付支付返回验签失败");
                    return AjaxResult.error("支付失败，请刷新后重试");
                } else {
                    log.error("调用宝付支付返回失败：{}", resultMap.get("returnMsg"));
                    return AjaxResult.error("支付失败，请刷新后重试");
                }
            }
        } catch (Exception e) {
            log.error("调用宝付支付处理返回异常!", e);
            return AjaxResult.error("支付异常，请返回刷新后重试");
        }
    }

    /**
     * 宝付下单（小程序）
     *
     * @param param
     * @return
     */
    @PostMapping("/applet-order")
    public AjaxResult appletOrder(@RequestBody WechatCodeParam param) {
        if (StringUtils.isAnyEmpty(param.getCode(), param.getState())) {
            return AjaxResult.error("缺少参数");
        }

        try {
            // 微信请求参数
            Map<String, Object> requestParam = new HashMap<>();
            requestParam.put("appid", baoFuPayConfig.getAppletPayAppId());
            requestParam.put("secret", baoFuPayConfig.getAppletPaySecret());
            requestParam.put("js_code", param.getCode());
            requestParam.put("grant_type", "authorization_code");

            // 根据code调用微信 auth.code2Session 接口，获取用户 openId 和 unionId
            String resultStr = HttpUtil.get("https://api.weixin.qq.com/sns/jscode2session", requestParam);
            if (StringUtils.isEmpty(resultStr)) {
                return AjaxResult.error("请求微信授权异常");
            }
            // 微信返回json转操作对象
            WeChatCodeDTO weChatCodeDTO = JSONUtil.parseObj(resultStr).toBean(WeChatCodeDTO.class);

            // 判断返回内容是否成功
            String errorMsg = WeChatUtil.handErrorCode(weChatCodeDTO.getErrcode());
            if (StringUtils.isNotEmpty(errorMsg)) {
                return AjaxResult.error(errorMsg);
            }

            // 用户小程序授权的 openId
            String openId = weChatCodeDTO.getOpenid();
            log.info("调用宝付前置微信授权! order: {}, openId: {}", param.getState(), openId);

            // redis 缓存KEY
            String stateKey = String.format(PayRedisConstants.BAOFU_STATE_KEY, param.getState());
            if (!redisTemplate.hasKey(stateKey)) {
                log.error("宝付微信支付失败，缓存订单已不存在");
                return AjaxResult.error("支付失败，请重新发起支付");
            } else {
                Object payInfo = redisTemplate.opsForValue().get(stateKey);
                if (null == payInfo) {
                    log.error("宝付微信支付失败，缓存订单为空");
                    return AjaxResult.error("支付失败，请重新发起支付");
                }
                // 缓存的支付信息
                WechatStateDTO wechatStateDTO = JSONUtil.toBean(payInfo.toString(), WechatStateDTO.class);

                // 支付类型
                int businessType = wechatStateDTO.getType();
                // 订单编号
                String orderCode = wechatStateDTO.getOrderCode();
                // 三方订单编号（因调起微信方式没有扩展参数，需要自处理业务类型），宝付订单编号不允许-号，所以此处使用_
                String thirdOrderCode = orderCode + "_" + businessType + RandomUtil.randomNumbers(4);

                TOnlinePayment tOnlinePayment = itOnlinePaymentService.queryByBusiness(businessType, orderCode, null);
                if (null == tOnlinePayment) {
                    log.error("宝付微信支付失败，在线支付信息不存在");
                    return AjaxResult.error("支付失败，请重新发起支付");
                }
                if (EPayStatus.PAID.getValue() == tOnlinePayment.getPayStatus()) {
                    // 在线支付信息已支付
                    log.error("宝付微信支付失败，在线支付信息已支付");
                    return AjaxResult.error("订单已支付");
                }

                // 实际支付金额（随机立减之后）
                BigDecimal amount = tOnlinePayment.getBusinessMoney();

                // 部分业务金额随机立减
                if (EPayBusinessType.getEnumByValue(businessType) == EPayBusinessType.RECHARGE
                        || EPayBusinessType.getEnumByValue(businessType) == EPayBusinessType.DIRECT_SELLING) {
                    // 充值
                    amount = PayUtil.handleAmountRandom(tOnlinePayment.getBusinessMoney());
                }

                // 请求时间戳
                String timeStr = DateUtils.parseDateTimeToStr(DateUtils.YAMMERERS, new Date());

                // 接口请求数据
                Map<String, Object> bizContent = new HashMap<>();
                bizContent.put("merId", baoFuPayConfig.getMerId());
                bizContent.put("terId", baoFuPayConfig.getTerId());

                // 订单编号
                bizContent.put("outTradeNo", thirdOrderCode);
                // 交易时间（年月日时分秒）
                bizContent.put("txnTime", timeStr);
                // 实际付款金额（分）
                bizContent.put("txnAmt", amount.multiply(new BigDecimal("100")).intValue());
                // 订单总金额（分）
                bizContent.put("totalAmt", amount.multiply(new BigDecimal("100")).intValue());
                // 订单超时时间（24小时）
                bizContent.put("timeExpire", "1440");
                // 普通产品（ORDINARY），发账产品（分账产品）
                bizContent.put("prodType", "ORDINARY");
                // 支付方式（微信JSAPI： WECHAT_JSAPI， 支付宝扫码支付：ALIPAY_NATIVE）
                bizContent.put("payCode", BaoFuPayConstants.WECHAT_JSAPI);
                // 支付属性
                Map<String, Object> payExtend = new HashMap<>();
                payExtend.put("body", "支付：" + orderCode);
                // APP跳转方式，小程序appId
                payExtend.put("sub_appid", baoFuPayConfig.getAppletPayAppId());
                payExtend.put("sub_openid", openId);
                bizContent.put("payExtend", payExtend);
                // 风控参数
                Map<String, String> riskMap = new HashMap<>();
                riskMap.put("riskInfo", InetAddress.getLocalHost().getHostAddress());
                bizContent.put("riskInfo", riskMap);
                // 二级商户号（测试传空，生产传SIMID）
                bizContent.put("subMchId", baoFuPayConfig.getSubMchId());
                // 支付成功回调地址
                bizContent.put("notifyUrl", baoFuPayConfig.getCallbackUrl());
                // 支付成功跳转页面
                bizContent.put("pageUrl", baoFuPayConfig.getFrontUrl());

                // 请求数据转json串
                String bizContentStr = JSONUtil.toJsonStr(bizContent);
                // 请求数据生成请求签名
                String signStr = SignatureUtils.encryptByRSA(bizContentStr, baoFuPayConfig.getPfxPath(), baoFuPayConfig.getPfxPwd());

                // 统一请求内容
                Map<String, Object> postMap = new HashMap<>();
                postMap.put("merId", baoFuPayConfig.getMerId());
                postMap.put("terId", baoFuPayConfig.getTerId());
                postMap.put("method", "unified_order");
                postMap.put("charset", CharsetKit.UTF_8);
                postMap.put("version", "1.0");
                postMap.put("format", "json");
                postMap.put("timestamp", timeStr);
                // 加密签名类型
                postMap.put("signType", "RSA");
                // 签名证书序列号，默认1
                postMap.put("signSn", "1");
                // 加密证书序列号，默认1
                postMap.put("ncrptnSn", "1");
                // 发送方私钥签名的非对称密钥，并做16进制转码
                postMap.put("signStr", signStr);
                // 业务数据（json字符串）
                postMap.put("bizContent", bizContentStr);

                log.info("宝付支付请求参数：{}", postMap);

                // 请求接口返回内容
                String resultPayStr = HttpUtil.post(baoFuPayConfig.getRequestUrl(), postMap);

                log.info("宝付支付返回参数：{}", resultPayStr);

                Map<String, String> resultMap = JSONUtil.toBean(resultPayStr, HashMap.class);

                if (BaoFuPayConstants.SUCCESS.equals(resultMap.get("returnCode"))) {
                    // 返回成功
                    if (SignatureUtils.verifySignature(baoFuPayConfig.getCerPath(), resultMap.get("dataContent"), resultMap.get("signStr"))) {
                        // 验签成功

                        // 返回的业务数据
                        Map<String, Object> dataMap = JSONUtil.toBean(resultMap.get("dataContent"), HashMap.class);

                        // 订单参数
                        String txnState = dataMap.get("txnState").toString();
                        if (BaoFuPayConstants.STATE_WAIT_PAYING.equals(txnState)) {
                            // 下单成功，处理返回参数
                            // 渠道返回参数
                            Map<String, String> chlRetParamMap = JSONUtil.toBean(dataMap.get("chlRetParam").toString(), HashMap.class);
                            // 前端使用支付参数
                            Map<String, String> payDataMap = JSONUtil.toBean(chlRetParamMap.get("wc_pay_data"), HashMap.class);

                            // 微信支付数据
                            return AjaxResult.success(payDataMap);
                        } else if (BaoFuPayConstants.SUCCESS.equals(txnState)) {
                            // 订单已交易成功
                            return AjaxResult.error("订单已交易成功，不能重付支付");
                        } else {
                            // 其它情况，订单状态异常
                            return AjaxResult.error("订单异常，请刷新后重试");
                        }
                    }
                    log.error("调用宝付支付返回验签失败");
                    return AjaxResult.error("支付失败，请刷新后重试");
                } else {
                    log.error("调用宝付支付返回失败：{}", resultMap.get("returnMsg"));
                    return AjaxResult.error("支付失败，请刷新后重试");
                }
            }
        } catch (Exception e) {
            log.error("调用宝付支付处理返回异常!", e);
            return AjaxResult.error("支付异常，请返回刷新后重试");
        }
    }

    /**
     * 查询订单详情
     *
     * @param orderCode 订单编号
     * @return
     */
    @GetMapping("/query-order/{orderCode}")
    public AjaxResult queryOrder(@PathVariable("orderCode") String orderCode) {
        try {
            QueryWrapper<TOnlinePayment> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("PAY_CHANNEL", EPayChannel.BAOFU.getValue());
            queryWrapper.eq("ORIGINAL_ORDER", orderCode);
            TOnlinePayment tOnlinePayment = itOnlinePaymentService.getOne(queryWrapper);
            if (null == tOnlinePayment) {
                return AjaxResult.error("支付信息不存在");
            }

            // 接口请求数据
            Map<String, Object> bizContent = new HashMap<>();
            bizContent.put("merId", baoFuPayConfig.getMerId());
            bizContent.put("terId", baoFuPayConfig.getTerId());
            // 订单编号
            bizContent.put("outTradeNo", orderCode);

            // 请求数据转json串
            String bizContentStr = JSONUtil.toJsonStr(bizContent);
            // 请求数据生成请求签名
            String signStr = SignatureUtils.encryptByRSA(bizContentStr, baoFuPayConfig.getPfxPath(), baoFuPayConfig.getPfxPwd());

            // 请求时间戳
            String timeStr = DateUtils.parseDateTimeToStr(DateUtils.YAMMERERS, new Date());

            Map<String, Object> postMap = new HashMap<>();
            postMap.put("merId", baoFuPayConfig.getMerId());
            postMap.put("terId", baoFuPayConfig.getTerId());
            postMap.put("method", "order_query");
            postMap.put("charset", CharsetKit.UTF_8);
            postMap.put("version", "1.0");
            postMap.put("format", "json");
            postMap.put("timestamp", timeStr);
            // 加密签名类型
            postMap.put("signType", "RSA");
            // 签名证书序列号，默认1
            postMap.put("signSn", "1");
            // 加密证书序列号，默认1
            postMap.put("ncrptnSn", "1");
            // 发送方私钥签名的非对称密钥，并做16进制转码
            postMap.put("signStr", signStr);
            // 业务数据（json字符串）
            postMap.put("bizContent", bizContentStr);

            log.info("宝付订单查询请求参数：{}", postMap);

            // 请求接口返回内容
            String resultStr = HttpUtil.post(baoFuPayConfig.getRequestUrl(), postMap);

            log.info("宝付订单查询返回参数：{}", resultStr);

            Map<String, String> resultMap = JSONUtil.toBean(resultStr, HashMap.class);

            if (BaoFuPayConstants.SUCCESS.equals(resultMap.get("returnCode"))) {
                // 返回成功
                if (SignatureUtils.verifySignature(baoFuPayConfig.getCerPath(), resultMap.get("dataContent"), resultMap.get("signStr"))) {
                    // 验签成功

                    // 返回的业务数据
                    Map<String, Object> dataMap = JSONUtil.toBean(resultMap.get("dataContent"), HashMap.class);

                    return AjaxResult.success(dataMap);
                }
                return AjaxResult.error("查询返回验签失败");
            } else {
                return AjaxResult.error("查询返回状态失败");
            }
        } catch (Exception e) {
            log.error("调用宝付查询订单异常!", e);
            return AjaxResult.error("调用宝付查询订单异常:" + e.getMessage());
        }
    }

}
