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

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hzs.common.core.enums.*;
import com.hzs.common.core.utils.CommonUtil;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.core.utils.StringUtils;
import com.hzs.common.domain.third.pay.TOnlinePayment;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAcc;
import com.hzs.common.domain.third.pay.TOnlinePaymentSepAccD;
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.AllInPayConfig;
import com.hzs.third.pay.config.JdPayBankProperties;
import com.hzs.third.pay.config.JdPayConfig;
import com.hzs.third.pay.config.JdPayWechatAlipayProperties;
import com.hzs.third.pay.constants.AllInPayConstants;
import com.hzs.third.pay.constants.JdPayConstants;
import com.hzs.third.pay.dto.RefundDTO;
import com.hzs.third.pay.jdpay.dto.*;
import com.hzs.third.pay.jdpay.sdk.JdPay;
import com.hzs.third.pay.jdpay.util.GsonUtil;
import com.hzs.third.pay.service.*;
import com.hzs.third.pay.util.JdPayUtil;
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 javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.*;

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

    @Autowired
    private AllInPayConfig allInPayConfig;
    @Autowired
    private IAllInPayService iAllInPayService;
    @Autowired
    private ITOnlineRefundService itOnlineRefundService;
    @Autowired
    private JdPayConfig jdPayConfig;

    @DubboReference
    ISaRefundServiceApi iSaRefundServiceApi;

    @Resource(name = "jdPayBank")
    private JdPay jdPay;
    @Autowired
    private JdPayBankProperties jdPayBankProperties;

    @Autowired
    private ITOnlinePaymentService itOnlinePaymentService;
    @Autowired
    private ITOnlinePaymentSepAccService sepAccService;
    @Autowired
    private ITOnlinePaymentSepAccDService sepAccDService;


    @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.getRefundCode(), 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;
    }


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

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

            Map<String, String> requestMap = new HashMap<>();
            requestMap.put("version", JdPayConstants.PAY_VERSION);
            // 商户编号
            requestMap.put("customerNum", jdPayConfig.getCustomerNum());
            // 店铺编号
            requestMap.put("shopNum", jdPayConfig.getShopNum());

            // 商户订单号
            requestMap.put("requestNum", tOnlinePayment.getOriginalOrder());
            // 商户退款订单号
            requestMap.put("refundRequestNum", refundCode);
            // 退款金额
            requestMap.put("refundPartAmount", amount.toString());
            // 退款回调地址
            requestMap.put("callbackUrl", jdPayConfig.getRefundCallbackUrl());

            String body = JSONUtil.toJsonStr(requestMap);

            log.info("京东退款请求参数： {}", body);

            String postResult = JdPayUtil.requestOrder(JdPayConstants.REFUND_ORDER, body, jdPayConfig.getSecretKey(), jdPayConfig.getAccessKey());

            log.info("京东退款返回数据： {}", postResult);

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

            if (JdPayConstants.RESULT_SUCCESS.equals(resultMap.get("result").toString())) {
                // 返回成功

                // 返回业务数据
                cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(resultMap.get("data"));

                // 申请退款成功，保存退款
                TOnlineRefund tOnlineRefund = new TOnlineRefund();
                tOnlineRefund.setRefundCode(refundCode);
                tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
                tOnlineRefund.setBusinessCode(refundDTO.getRefundBusinessCode());
                tOnlineRefund.setRefundMoney(amount);
                tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
                tOnlineRefund.setRefundNumber(jsonObject.getStr("orderNum"));
                tOnlineRefund.setPkCountry(refundDTO.getPkCountry());
                tOnlineRefund.setPkCreator(refundDTO.getUserId());
                if (itOnlineRefundService.save(tOnlineRefund)) {
                    return null;
                }
                log.error("调用京东退款入库失败， tOnlineRefund: {}", tOnlineRefund);
            } else {
                log.error("调用京东退款返回失败：{}", resultMap.get("errorMsg"));
            }
            return "退款失败，请重试";
        } catch (Exception e) {
            log.error("调用京东退款处理返回异常!", e);
            return "退款异常，请重试";
        }
    }

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

            // 退款编号
            String refundCode = CommonUtil.createSerialNumber(EOrderPrefix.REFUND_CODE);
            // 当前时间
            Date nowDate = new Date();

            JdPayRefundRequest request = JdPayRefundRequest.builder()
                    // 退款订单号
                    .outTradeNo(refundCode)
                    // 原订单号（需要退款的订单号）
                    .originalOutTradeNo(tOnlinePayment.getBusinessCode())
                    // 退款时间
                    .tradeDate(DateUtils.parseDateToFormat(DateUtils.YAMMERERS, nowDate))
                    // 退款金额
                    .tradeAmount(amount.multiply(new BigDecimal("100")).intValue() + "")
                    // 货币种类
                    .currency("CNY")
                    // 退款回调地址
                    .notifyUrl(jdPayBankProperties.getRefundNotifyUrl())
                    // 回传信息
//                    .returnParams("")
                    .build();

            // 请求京东退款接口
            JdPayRefundResponse response = jdPay.refund(request);

            if ("00000".equals(response.getResultCode())) {
                // 请求响应成功
                // 申请退款成功，保存退款
                TOnlineRefund tOnlineRefund = new TOnlineRefund();
                tOnlineRefund.setRefundCode(refundCode);
                tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
                tOnlineRefund.setBusinessCode(refundDTO.getRefundBusinessCode());
                tOnlineRefund.setRefundMoney(amount);
                tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
                tOnlineRefund.setRefundNumber(response.getTradeNo());
                tOnlineRefund.setPkCountry(refundDTO.getPkCountry());
                tOnlineRefund.setPkCreator(refundDTO.getUserId());
                if (itOnlineRefundService.save(tOnlineRefund)) {
                    return null;
                } else {
                    log.error("调用京东收银台退款入库处理失败，desc: {}", response.getResultDesc());
                    return "退款失败，请联系系统管理员处理";
                }
            }
            log.error("调用京东收银台退款处理失败，desc: {}", response.getResultDesc());
            return "退款失败，请重试";
        } catch (Exception e) {
            log.error("调用京东收银台退款处理返回异常!", e);
            return "退款异常，请重试";
        }
    }

    @Override
    public String jdRefundDivision(String orderCode, Long userId) {
        TOnlinePaymentSepAcc sepAcc = sepAccService.getSepAccByOrderCode(orderCode);
        LambdaQueryWrapper<TOnlinePaymentSepAccD> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TOnlinePaymentSepAccD::getPkSepAcc, sepAcc.getPkId());
        queryWrapper.eq(TOnlinePaymentSepAccD::getDelFlag, EDelFlag.UN_DELETE.getValue());
        queryWrapper.orderByAsc(TOnlinePaymentSepAccD::getPkId);
        LambdaQueryWrapper<TOnlinePayment> paymentQueryWrapper = new LambdaQueryWrapper<>();
        paymentQueryWrapper.eq(TOnlinePayment::getBusinessCode, orderCode);
        TOnlinePayment tOnlinePayment = itOnlinePaymentService.getOne(paymentQueryWrapper, false);
        List<TOnlinePaymentSepAccD> sepAccDList = sepAccDService.list(queryWrapper);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String now = sdf.format(new Date());
        String refundCode = "R_" + orderCode;
        JdPayRefundRequest request = JdPayRefundRequest.builder()
                .outTradeNo("R_" + orderCode)
                .originalOutTradeNo(orderCode)
                .tradeAmount(sepAcc.getTradeAmount().multiply(new BigDecimal("100")).intValue() + "")
                .currency("CNY")
                .tradeDate(now)
                .returnParams("")
                .notifyUrl(jdPayBankProperties.getRefundNotifyUrl())
                .build();

        JdPayDivisionAccountRefund divisionAccountRefund = new JdPayDivisionAccountRefund();
        List<JdPayDivisionAccountRefundInfo> divisionAccountTradeInfoList = new ArrayList<>();
        for (int i = 0; i < sepAccDList.size(); i++) {
            TOnlinePaymentSepAccD sepAccD = sepAccDList.get(i);
            JdPayDivisionAccountRefundInfo divisionAccountTradeInfo = new JdPayDivisionAccountRefundInfo();
            divisionAccountTradeInfo.setMerchantNo(sepAccD.getAccount());
            divisionAccountTradeInfo.setOutTradeNo("R_" + orderCode + "_" + ( i + 1 ));
            divisionAccountTradeInfo.setOriginalOutTradeNo(sepAccD.getOutTradeNo());
            divisionAccountTradeInfo.setTradeAmount(sepAccD.getTradeAmount().multiply(new BigDecimal("100")).intValue() + "");
            divisionAccountTradeInfoList.add(divisionAccountTradeInfo);
        }
        divisionAccountRefund.setDivisionAccountRefundInfoList(divisionAccountTradeInfoList);
        request.setDivisionAccountRefund(GsonUtil.toJson(divisionAccountRefund));
        try {
            JdPayRefundResponse response = jdPay.refund(request);
            log.info("JdPayRefundResponse -> {}", JSONUtil.toJsonStr(response));
            TOnlineRefund tOnlineRefund = new TOnlineRefund();
            tOnlineRefund.setRefundCode(refundCode);
            tOnlineRefund.setPkOnlinePayment(tOnlinePayment.getPkId());
            tOnlineRefund.setBusinessCode(tOnlinePayment.getBusinessCode());
            tOnlineRefund.setRefundMoney(tOnlinePayment.getPayMoney());
            tOnlineRefund.setRefundChannel(tOnlinePayment.getPayChannel());
            tOnlineRefund.setRefundNumber(response.getTradeNo());
            tOnlineRefund.setPkCountry(1);
            tOnlineRefund.setPkCreator(userId);
            if ("0000".equals(response.getResultCode())) {
                log.info("提交退款成功");
                itOnlineRefundService.save(tOnlineRefund);
                return "提交退款成功";
            } else {
                tOnlineRefund.setCallbackInfo(response.getResultDesc());
                itOnlineRefundService.save(tOnlineRefund);
                log.error("提交退款失败! {}", JSONUtil.toJsonStr(response));
                return "提交退款失败!["+response.getResultDesc()+"]";
            }
        } catch (Exception e) {
            return "提交退款异常! ["+e.getMessage()+"]";
        }
    }

    @Override
    public String jdQueryEnterpriseWalletBalance() {
        JdPayQueryEnterpriseWalletBalanceRequest request1 = JdPayQueryEnterpriseWalletBalanceRequest.builder()
                .accountNo("153428607005")
                .build();
        JdPayQueryEnterpriseWalletBalanceRequest request2 = JdPayQueryEnterpriseWalletBalanceRequest.builder()
                .accountNo("153428607007")
                .build();
        try {
            JdPayQueryEnterpriseWalletBalanceResponse response1 = jdPay.queryEnterpriseWalletBalance(request1);
            log.info("response1 -> {}", JSONUtil.toJsonStr(response1));
            JdPayQueryEnterpriseWalletBalanceResponse response2 = jdPay.queryEnterpriseWalletBalance(request2);
            log.info("response2 -> {}", JSONUtil.toJsonStr(response2));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return "";
    }
}
