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

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.huifu.adapay.model.Refund;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.*;
import com.hzs.common.core.text.CharsetKit;
import com.hzs.common.core.utils.*;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.domain.third.pay.TOnlineRefund;
import com.hzs.common.domain.third.pay.ext.TOnlineRefundExt;
import com.hzs.sale.refund.ISaRefundServiceApi;
import com.hzs.third.pay.config.*;
import com.hzs.third.pay.constants.*;
import com.hzs.third.pay.dto.RefundDTO;
import com.hzs.third.pay.service.IAllInPayService;
import com.hzs.third.pay.service.IRefundService;
import com.hzs.third.pay.service.ITOnlineRefundService;
import com.hzs.third.pay.util.baofu.SignatureUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.text.DecimalFormat;
import java.util.*;

/**
 * 基础退款服务
 */
@Slf4j
@Service
public class RefundServiceImpl implements IRefundService {

    @Autowired
    private AdaPayConfig adaPayConfig;
    @Autowired
    private BaoFuPayConfig baoFuPayConfig;
    @Autowired
    private AllInPayConfig allInPayConfig;
    @Autowired
    private IAllInPayService iAllInPayService;
    @Autowired
    private ITOnlineRefundService itOnlineRefundService;

    @DubboReference
    ISaRefundServiceApi iSaRefundServiceApi;

    @Override
    public String adaRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment) {
        try {
            // 退款金额（默认按全款退）
            BigDecimal amount;
            if (null != refundDTO.getRefundAmount()) {
                // 如果传入实际退款金额，则按实际退款金额处理
                amount = refundDTO.getRefundAmount();
            } else {
                return "未申请退款金额";
            }

            // 退款编号
            String refundCode = CommonUtil.createSerialNumber(EOrderPrefix.REFUND_CODE);

            Map<String, Object> refundParams = new HashMap<>();
            // 退款编号
            refundParams.put("refund_order_no", refundCode);
            DecimalFormat df = new DecimalFormat("#0.00");
            // 退款金额
            refundParams.put("refund_amt", df.format(amount.doubleValue()));
            // 前端设备信息
            Map<String, Object> deviceInfoMap = new HashMap<>();
            deviceInfoMap.put("device_type", "4");
            deviceInfoMap.put("device_ip", InetAddress.getLocalHost().getHostAddress());
            refundParams.put("device_info", deviceInfoMap);
            refundParams.put("notify_url", adaPayConfig.getRefundCallbackUrl());

            log.info("汇付退款请求参数：{}", refundParams);

            Map<String, Object> resultData = Refund.create(tOnlinePayment.getPayNumber(), refundParams, adaPayConfig.getAppId());

            log.info("汇付退款返回参数：{}", resultData);

            if (AdaPayConstants.SUCCESS.equals(resultData.get("status"))
                    || AdaPayConstants.PENDING.equals(resultData.get("status"))) {
                // 申请退款成功，保存退款
                TOnlineRefund tOnlineRefund = new TOnlineRefund();
                tOnlineRefund.setRefundCode(refundCode);
                tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
                tOnlineRefund.setBusinessCode(refundDTO.getRefundBusinessCode());
                tOnlineRefund.setRefundMoney(amount);
                tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
                tOnlineRefund.setRefundNumber(resultData.get("id").toString());
                tOnlineRefund.setPkCountry(refundDTO.getPkCountry());
                tOnlineRefund.setPkCreator(refundDTO.getUserId());
                if (itOnlineRefundService.save(tOnlineRefund)) {
                    return null;
                }
                log.error("调用汇付退款入库失败， tOnlineRefund: {}", tOnlineRefund);
                return "退款失败，请重试";
            } else {
                log.error("调用汇付退款返回失败! code: {}, msg: {}, invalid_param: {}", resultData.get("error_code"), resultData.get("error_msg"), resultData.get("invalid_param"));
                return "退款失败，请重试";
            }
        } catch (Exception e) {
            log.error("调用汇付退款处理返回异常!", e);
            return "退款异常，请重试";
        }
    }

    @Override
    public String baoFuRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment) {
        try {
            // 退款金额（默认按全款退）
            BigDecimal amount = tOnlinePayment.getPayMoney();
            if (null != refundDTO.getRefundAmount()) {
                // 如果传入实际退款金额，则按实际退款金额处理
                amount = refundDTO.getRefundAmount();
            }

            // 退款编号
            String refundCode = CommonUtil.createSerialNumber(EOrderPrefix.REFUND_CODE);
            // 请求时间戳
            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("originOutTradeNo", tOnlinePayment.getOriginalOrder());
            // 退款编号
            bizContent.put("outTradeNo", refundCode);
            // 退款金额（分）
            bizContent.put("refundAmt", amount.multiply(new BigDecimal("100")).intValue());
            // 退款总金额（分）
            bizContent.put("totalAmt", amount.multiply(new BigDecimal("100")).intValue());
            // 交易时间（年月日时分秒）
            bizContent.put("txnTime", timeStr);
            // 退款原因
            bizContent.put("refundReason", "用户申请退款");
            // 退款成功回调地址
            bizContent.put("notifyUrl", baoFuPayConfig.getRefundCallbackUrl());

            // 请求数据转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", "order_refund");
            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);

                    // 返回状态正常
                    if (BaoFuPayConstants.STATE_REFUND.equals(dataMap.get("refundState").toString())) {
                        // 申请退款成功，保存退款
                        TOnlineRefund tOnlineRefund = new TOnlineRefund();
                        tOnlineRefund.setRefundCode(refundCode);
                        tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
                        tOnlineRefund.setBusinessCode(refundDTO.getRefundBusinessCode());
                        tOnlineRefund.setRefundMoney(amount);
                        tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
                        tOnlineRefund.setRefundNumber(dataMap.get("tradeNo").toString());
                        tOnlineRefund.setPkCountry(refundDTO.getPkCountry());
                        tOnlineRefund.setPkCreator(refundDTO.getUserId());
                        if (itOnlineRefundService.save(tOnlineRefund)) {
                            return null;
                        }
                        log.error("调用宝付退款入库失败， tOnlineRefund: {}", tOnlineRefund);
                        return "退款失败，请重试";
                    }
                    log.error("调用宝付申请退款失败");
                    return "申请退款失败，请重试";
                }
                log.error("调用宝付退款返回验签失败");
                return "退款失败，请重试";
            } else {
                log.error("调用宝付退款返回失败：{}", resultMap.get("returnMsg"));
                return "退款失败，请重试";
            }
        } catch (Exception e) {
            log.error("调用宝付退款处理返回异常!", e);
            return "退款异常，请重试";
        }
    }

    @Override
    public String allInRefundHandle(RefundDTO refundDTO, TOnlinePayment tOnlinePayment) {
        try {
            // 退款编号
            String refundCode = CommonUtil.createSerialNumber(EOrderPrefix.REFUND_CODE);

            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("reqip", InetAddress.getLocalHost().getHostAddress());
            params.put("reqtime", DateUtils.parseDateTimeToStr(DateUtils.YAMMERERS, new Date()));

            // 退款金额（分）
            params.put("trxamt", refundDTO.getRefundAmount().multiply(new BigDecimal("100")).intValue() + "");
            // 退款单号
            params.put("reqsn", refundCode);
            // 原交易订单号 和 原交易流水号 必传一个
            // 原交易订单号
            params.put("oldreqsn", tOnlinePayment.getOriginalOrder());
            // 原交易流水号
            params.put("oldtrxid", tOnlinePayment.getPayNumber());

            log.info("通联退款请求参数： {}", params);

            // 退款状态默认为退款中
            // 判断如果是当天的订单使用撤销，非当天的使用退款
            Map<String, String> resultMap;
            if (DateUtils.parseDateToFormat(DateUtils.YAMMER, new Date()).equals(DateUtils.parseDateToFormat(DateUtils.YAMMER, tOnlinePayment.getCreationTime()))) {
                // 当天订单，撤销； 撤销订单实时生效可以不用等回调直接更新状态
                log.info("通联订单撤销");
                resultMap = iAllInPayService.doRequest(allInPayConfig.getRequestUrl() + "/tranx/cancel", params);
            } else {
                // 非当天订单，退款
                log.info("通联订单退款");
                resultMap = iAllInPayService.doRequest(allInPayConfig.getRequestUrl() + "/tranx/refund", params);
            }

            log.info("通联退款返回数据： {}", resultMap);

            if (AllInPayConstants.SUCCESS_CODE.equals(resultMap.get("trxstatus"))
                    || AllInPayConstants.SUCCESS_2000.equals(resultMap.get("trxstatus"))) {
                // 返回成功，通联没有退款回调，接口调用成功就为退款成功
                TOnlineRefund tOnlineRefund = new TOnlineRefund();
                tOnlineRefund.setRefundCode(refundCode);
                tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
                tOnlineRefund.setBusinessCode(refundDTO.getRefundBusinessCode());
                tOnlineRefund.setRefundMoney(tOnlinePayment.getPayMoney());
                tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
                tOnlineRefund.setRefundNumber(resultMap.get("trxid"));
                tOnlineRefund.setRefundStatus(ERefundStatus.REFUNDED.getValue());
                tOnlineRefund.setFinishTime(DateUtils.parseDateOne(resultMap.get("fintime"), DateUtils.YAMMERERS));
                tOnlineRefund.setFinishMoney(tOnlinePayment.getPayMoney());
                tOnlineRefund.setPkCountry(refundDTO.getPkCountry());
                tOnlineRefund.setPkCreator(refundDTO.getUserId());
                if (itOnlineRefundService.save(tOnlineRefund)) {
                    return null;
                }
                log.error("调用通联退款入库失败， tOnlineRefund: {}", tOnlineRefund);
            } else {
                log.error("调用通联退款入库失败， tOnlineRefund: {}", resultMap.get("retmsg"));
            }
            return "退款失败，请重试";
        } catch (Exception e) {
            log.error("调用通联退款处理返回异常!", e);
            return "退款异常，请重试";
        }
    }

    @Override
    public boolean notifyHandle(EPayChannel ePayChannel, String refundCode, String refundNumber, Date finishTime, BigDecimal finishMoney) {
        try {
            // 查询退款信息
            TOnlineRefundExt onlineRefund = this.queryOnlineRefund(refundCode, ePayChannel, refundNumber);
            if (null == onlineRefund || ERefundStatus.REFUNDED.getValue() == onlineRefund.getRefundStatus()) {
                log.error("退款回调成功: 信息不存在或已退款");
                return false;
            }

            if (onlineRefund.getRefundMoney().compareTo(finishMoney) > 0) {
                // 退款金额 大于 退款到账金额
                log.warn("退款回调成功: 退款金额大于退款到账金额，退款编号：{}, 退款到账金额：{}", onlineRefund.getRefundCode(), finishMoney);
            }

            // 更新退款信息（退款成功）
            onlineRefund.setRefundStatus(ERefundStatus.REFUNDED.getValue());
            onlineRefund.setRefundNumber(refundNumber);
            onlineRefund.setFinishTime(finishTime);
            onlineRefund.setFinishMoney(finishMoney);
            onlineRefund.setErrorMsg("");
            // 更新退款相关信息
            if (itOnlineRefundService.updateById(onlineRefund)) {

                // 退款业务处理
                if (StringUtils.isNotEmpty(onlineRefund.getPayBusinessCode())) {
                    // 如果退款业务单号存在则进行业务回调处理，如果不存在则表示直接根据支付信息进行退款，不需要进行业务回调
                    if (this.handleBusiness(onlineRefund)) {
                        // 更新业务处理信息
                        if (itOnlineRefundService.updateById(onlineRefund)) {
                            return true;
                        }
                    }
                }
                return true;
            }
        } catch (Exception e) {
            log.error("退款回调成功: 处理异常", e);
        }
        return false;
    }

    @Override
    public boolean notifyErrorHandle(EPayChannel ePayChannel, String refundCode, String refundNumber, String errorMsg) {
        try {
            // 查询退款信息
            TOnlineRefundExt onlineRefund = this.queryOnlineRefund(refundCode, ePayChannel, refundNumber);
            if (null == onlineRefund || ERefundStatus.REFUNDED.getValue() == onlineRefund.getRefundStatus()) {
                log.error("退款回调失败: 信息不存在或已退款");
                return false;
            }

            // 更新退款信息（退款失败）
            onlineRefund.setRefundStatus(ERefundStatus.REFUND_FAILED.getValue());
            onlineRefund.setRefundNumber(refundNumber);
            onlineRefund.setErrorMsg(errorMsg);
            if (itOnlineRefundService.updateById(onlineRefund)) {
                // 退款业务处理
                if (StringUtils.isNotEmpty(onlineRefund.getBusinessCode())) {
                    // 如果退款业务单号存在则进行业务回调处理，如果不存在则表示直接根据支付信息进行退款，不需要进行业务回调
                    if (this.handleBusiness(onlineRefund)) {
                        // 更新业务处理信息
                        if (itOnlineRefundService.updateById(onlineRefund)) {
                            return true;
                        }
                    }
                }
                return true;
            }
        } catch (Exception e) {
            log.error("退款回调失败: 处理异常", e);
        }
        return false;
    }

    /**
     * 查询退款信息
     *
     * @param refundCode   退款编号
     * @param ePayChannel  退款渠道
     * @param refundNumber 退款流水号
     * @return
     */
    private TOnlineRefundExt queryOnlineRefund(String refundCode, EPayChannel ePayChannel, String refundNumber) {
        // 获取退款信息
        if (StringUtils.isNotEmpty(refundCode)) {
            return itOnlineRefundService.queryRefundAndPay(refundCode, null, null);
        } else {
            return itOnlineRefundService.queryRefundAndPay(null, ePayChannel.getValue(), refundNumber);
        }
    }

    /**
     * 退款业务处理
     *
     * @param onlineRefund 退款数据
     * @return
     */
    private boolean handleBusiness(TOnlineRefundExt onlineRefund) {
        // 业务类型
        EPayBusinessType businessType = EPayBusinessType.getEnumByValue(onlineRefund.getPayBusinessType());
        if (null != businessType) {
            // 服务调用结果
            Boolean resultBool = null;
            // 服务调用返回信息
            String resultMsg = "";
            // 根据业务类型调用具体业务处理 dubbo 服务
            switch (businessType) {
                case DIRECT_SELLING:
                    // 直销订单
                    R<String> saRefundR = iSaRefundServiceApi.onlinePayRefundCallback(onlineRefund.getPayBusinessCode(), onlineRefund.getBusinessCode(), onlineRefund.getRefundStatus(), onlineRefund.getErrorMsg());
                    if (saRefundR.isSuccess()) {
                        resultBool = true;
                    } else {
                        resultBool = false;
                        resultMsg = saRefundR.getMsg();
                    }
                    break;
                default:
            }

            if (null != resultBool) {
                if (resultBool) {
                    // 调用业务处理成功、失败，如果失败记录原因
                    onlineRefund.setCallbackStatus(ECallbackStatus.SUCCESS.getValue());
                    onlineRefund.setCallbackInfo("");
                } else {
                    onlineRefund.setCallbackStatus(ECallbackStatus.FAIL.getValue());
                    onlineRefund.setCallbackInfo(resultMsg);
                }
            } else {
                log.error("退款回调没有后续业务处理");
                onlineRefund.setCallbackInfo("退款回调没有后续业务处理");
            }
            return true;

        }
        log.error("退款业务处理失败，不存在的业务类型");
        return false;
    }

}
