package com.yida.data.mall.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yida.data.common.core.entity.constant.UnionPayConstant;
import com.yida.data.common.core.entity.constant.WxPayConstant;
import com.yida.data.common.core.entity.mall.EduBillOrder;
import com.yida.data.common.core.entity.mall.EduBillOrderRefund;
import com.yida.data.common.core.entity.system.EduPayWxConfig;
import com.yida.data.common.core.entity.system.UnionPayConfig;
import com.yida.data.common.core.enums.*;
import com.yida.data.common.core.utils.EnumUtils;
import com.yida.data.common.core.utils.UnionPayPublicUtil;
import com.yida.data.common.core.utils.UnionPayUtil;
import com.yida.data.common.core.utils.WxPayUtil;
import com.yida.data.common.service.CommonService;
import com.yida.data.mall.mapper.EduBillOrderMapper;
import com.yida.data.mall.service.EduBillOrderRefundService;
import com.yida.data.mall.service.EduBillOrderService;
import com.yida.data.mall.service.H5EduBillService;
import com.yida.data.mall.vo.BillRefundOrderVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 账单订单表 Service实现
 *
 * @author ZYJ
 * @date 2022-03-31 14:49:59
 */
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class EduBillOrderServiceImpl extends ServiceImpl
        <EduBillOrderMapper, EduBillOrder> implements EduBillOrderService {

    private final CommonService commonService;
    private final UnionPayPublicUtil unionPayPublicUtil;

    @Lazy
    @Resource
    private H5EduBillService h5EduBillService;

    private final EduBillOrderRefundService eduBillOrderRefundService;

    @Override
    public BigDecimal sumPayOrderPrice(Long billId, Long studentId) {
        return baseMapper.sumPayOrderPrice(billId, studentId);
    }

    @Override
    public Long sumPayStudentNumber(Long billId) {
        return baseMapper.sumPayStudentNumber(billId);
    }

    @Override
    public void updateBillOrderStatus() {
        // 查询超时订单
        List<EduBillOrder> list = baseMapper.selectTimeOutBillOrder();
        if (CollUtil.isNotEmpty(list)) {
            for (EduBillOrder order : list) {
                // 微信订单
                if (PayWay.WEI_XIN.getValue().equals(order.getPayWay())) {
                    updateWxBillOrderStatus(order);
                } else if (PayWay.UNION.getValue().equals(order.getPayWay())) {
                    updateUnionBillOrderStatus(order);
                }
            }
        }
    }

    /**
     * 更新微信超时账单订单状态
     *
     * @param order 订单信息
     * @author ZYJ
     * @date 2022/6/27 19:28
     */
    private void updateWxBillOrderStatus(EduBillOrder order) {
        try {
            // 查询学校微信支付配置信息
            EduPayWxConfig config = commonService.getPayWxConfigBySchool(order.getDeptId());
            // 主动查询订单支付状态
            JSONObject resultJson = JSONUtil.parseObj(WxPayUtil.queryOrder(config, order.getOrderCode()));
            log.info("微信账单订单查询返回数据, billOrderCode: {}, resultJson: {}", order.getOrderCode(), resultJson);
            if (resultJson.containsKey(WxPayConstant.CODE_NAME)
                    && WxPayConstant.ORDER_NOT_EXIST.equals(resultJson.getStr(WxPayConstant.CODE_NAME))) {
                // 订单不存在  取消订单
                order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
            } else {
                String tradeState = resultJson.getStr("trade_state");
                // 未支付则关闭订单
                if (WxTradeStateEnum.NOT_PAY.getStatus().equals(tradeState)) {
                    WxPayUtil.closeOrder(config, order.getOrderCode());
                }
                WxTradeStateEnum wxTradeStateEnum = EnumUtils.valueOf(WxTradeStateEnum.class, tradeState,
                        WxTradeStateEnum.class.getMethod("getStatus"));
                switch (Objects.requireNonNull(wxTradeStateEnum)) {
                    // 支付成功情况
                    case SUCCESS:
                        h5EduBillService.billOrderPaySuccess(order.getOrderCode(),
                                DateUtil.parseLocalDateTime(resultJson.getStr("success_time"),
                                        "yyyy-MM" + "-dd'T" + "'HH:mm" + ":ssXXX"));
                        break;
                    // 作废订单
                    case NOT_PAY:
                    case CLOSED:
                    case REVOKED:
                    case PAY_ERROR:
                        order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                        order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                        break;
                    default:
                        break;
                }
            }
            if (OrderStatusEnum.CLOSED.getStatus().equals(order.getOrderStatus())) {
                baseMapper.updateById(order);
            }
        } catch (Exception e) {
            log.error("更新微信账单订单状态失败, order: {}", order, e);
        }
    }

    /**
     * 更新银联超时账单订单状态
     *
     * @param order 订单信息
     * @author ZYJ
     * @date 2022/6/27 19:28
     */
    private void updateUnionBillOrderStatus(EduBillOrder order) {
        try {
            // 查询学校银联配置信息
            UnionPayConfig config = commonService.getUnionPayConfigByDept(order.getDeptId());
            // 主商户信息
            UnionPayConfig parentConfig = config.getParentConfig();
            // 开发者ID
            String appId = Objects.nonNull(parentConfig) ? parentConfig.getAppId() : config.getAppId();
            // 开发者key
            String appKey = Objects.nonNull(parentConfig) ? parentConfig.getAppKey() : config.getAppKey();

            // 主动查询订单支付状态
            JSONObject resultJson = UnionPayUtil.queryOrder(config, unionPayPublicUtil.getAccessToken(appId, appKey), order.getOrderCode());
            log.info("银联账单订单查询返回数据, billOrderCode: {}, resultJson: {}", order.getOrderCode(), resultJson);
            // 返回code
            String code = (String) resultJson.get(UnionPayConstant.CODE_NAME);
            // 无法找到指定的订单 需要直接关闭订单
            if (UnionPayConstant.NO_ORDER_CODE.equalsIgnoreCase(code)
                    || UnionPayConstant.TARGET_FAIL_CODE.equalsIgnoreCase(code)) {
                order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                baseMapper.updateById(order);
                return;
            }
            // 支付状态
            String status = (String) resultJson.get("status");
            // 未支付则关闭订单 (银联云闪付不支付关闭订单)
            if (UnionTradeStateEnum.NEW_ORDER.getStatus().equals(status)
                    && !UnionPayTypeEnum.UNION_H5_TYPE.getType().equals(order.getPayType())) {
                UnionPayUtil.closeOrder(config, unionPayPublicUtil.getAccessToken(appId, appKey), order.getOrderCode());
            }
            UnionTradeStateEnum tradeStateEnum = EnumUtils.valueOf(UnionTradeStateEnum.class, status,
                    UnionTradeStateEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(tradeStateEnum)) {
                // 支付成功
                case TRADE_SUCCESS:
                    h5EduBillService.billOrderPaySuccess(order.getOrderCode(),
                            LocalDateTimeUtil.parse((String) resultJson.get("payTime"), DatePattern.NORM_DATETIME_PATTERN));
                    break;
                // 作废订单
                case NEW_ORDER:
                case TRADE_CLOSED:
                case WAIT_BUYER_PAY:
                    order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                    break;
                default:
                    break;
            }
            if (OrderStatusEnum.CLOSED.getStatus().equals(order.getOrderStatus())) {
                baseMapper.updateById(order);
            }
        } catch (Exception e) {
            log.error("更新银联账单订单状态失败, order: {}", order, e);
        }
    }

    @Override
    public void updateRefundStatus() {
        // 查询退款中状态的账单订单
        List<BillRefundOrderVO> refundOrderList = baseMapper.selectRefundOrder();
        if (CollUtil.isNotEmpty(refundOrderList)) {
            refundOrderList.forEach(billRefundOrderVO -> {
                // 微信
                if (PayWay.WEI_XIN.getValue().equals(billRefundOrderVO.getPayWay())) {
                    updateWxRefundStatus(billRefundOrderVO);
                } else if (PayWay.UNION.getValue().equals(billRefundOrderVO.getPayWay())) {
                    updateUnionRefundStatus(billRefundOrderVO);
                }
            });
        }
    }

    /**
     * 更新微信退款订单状态
     *
     * @param billRefundOrderVO 退款信息
     * @author ZYJ
     * @date 2022/6/27 19:44
     */
    private void updateWxRefundStatus(BillRefundOrderVO billRefundOrderVO) {
        try {
            EduPayWxConfig config = commonService.getPayWxConfigBySchool(billRefundOrderVO.getDeptId());
            // 主动查询退款状态
            JSONObject resultJson = JSONUtil.parseObj(WxPayUtil.queryRefund(config, billRefundOrderVO.getRefundCode()));
            log.info("账单订单退款查询返回数据, refundOrderCode: {}, resultJson: {}", billRefundOrderVO.getRefundCode(), resultJson);
            // 状态
            String status = resultJson.getStr("status");
            WxRefundStatusEnum wxRefundStatusEnum = EnumUtils.valueOf(WxRefundStatusEnum.class, status,
                    WxRefundStatusEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(wxRefundStatusEnum)) {
                case REFUND_SUCCESS:
                    // 更新订单退款状态
                    EduBillOrder eduBillOrder = getById(billRefundOrderVO.getOrderId());
                    eduBillOrder.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    eduBillOrder.setPayStatus(PayStatusEnum.REFUND_SUCCESS.getStatus());
                    eduBillOrder.setIsRefund(RefundStatusEnum.IS_REFUND.getStatus());
                    baseMapper.updateById(eduBillOrder);
                    // 更新退款时间
                    EduBillOrderRefund eduBillOrderRefund = eduBillOrderRefundService.getById(billRefundOrderVO.getRefundId());
                    eduBillOrderRefund.setRefundTime(DateUtil.parseLocalDateTime(resultJson.getStr("success_time"), "yyyy-MM-dd'T'HH:mm:ssXXX"));
                    eduBillOrderRefundService.updateById(eduBillOrderRefund);
                    break;
                case REFUND_ABNORMAL:
                    // TODO 2022/6/27 需要后续处理退款失败情况
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("查询微信账单订单退款状态失败, refundOrder: {}", billRefundOrderVO, e);
        }
    }

    /**
     * 更新银联退款订单状态
     *
     * @param billRefundOrderVO 退款信息
     * @author ZYJ
     * @date 2022/6/27 19:44
     */
    private void updateUnionRefundStatus(BillRefundOrderVO billRefundOrderVO) {
        try {
            // 查询学校银联配置信息
            UnionPayConfig config = commonService.getUnionPayConfigByDept(billRefundOrderVO.getDeptId());
            // 主商户信息
            UnionPayConfig parentConfig = config.getParentConfig();
            // 开发者ID
            String appId = Objects.nonNull(parentConfig) ? parentConfig.getAppId() : config.getAppId();
            // 开发者key
            String appKey = Objects.nonNull(parentConfig) ? parentConfig.getAppKey() : config.getAppKey();
            // 主动查询退款状态
            JSONObject resultJson = UnionPayUtil.refundQuery(config, billRefundOrderVO.getRefundCode(), unionPayPublicUtil.getAccessToken(appId, appKey));
            log.info("银联账单订单退款查询返回数据, refundOrderCode: {}, resultJson: {}", billRefundOrderVO.getRefundCode(), resultJson);
            // 退款状态
            String refundStatus = resultJson.getStr("refundStatus");
            UnionRefundStatusEnum refundStatusEnum = EnumUtils.valueOf(UnionRefundStatusEnum.class, refundStatus,
                    UnionRefundStatusEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(refundStatusEnum)) {
                case REFUND_SUCCESS:
                    // 更新订单退款状态
                    EduBillOrder eduBillOrder = getById(billRefundOrderVO.getOrderId());
                    eduBillOrder.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    eduBillOrder.setPayStatus(PayStatusEnum.REFUND_SUCCESS.getStatus());
                    eduBillOrder.setIsRefund(RefundStatusEnum.IS_REFUND.getStatus());
                    baseMapper.updateById(eduBillOrder);
                    // 更新退款时间
                    EduBillOrderRefund eduBillOrderRefund = eduBillOrderRefundService.getById(billRefundOrderVO.getRefundId());
                    eduBillOrderRefund.setRefundTime(LocalDateTimeUtil.parse((String) resultJson.get("responseTimestamp"), DatePattern.NORM_DATETIME_PATTERN));
                    eduBillOrderRefundService.updateById(eduBillOrderRefund);
                    break;
                case REFUND_FAIL:
                    // TODO 2022/6/27 需要后续处理退款失败情况
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("查询银联账单订单退款状态失败, refundOrder: {}", billRefundOrderVO, e);
        }
    }

    @Override
    public String unionPayNotify(Map<String, Object> map) {
        try {
            // 根据订单号查询对应的订单信息
            EduBillOrder order = getOne(new LambdaQueryWrapper<>(new EduBillOrder()).eq(EduBillOrder::getOrderCode, map.get("merOrderId")));
            if (Objects.isNull(order)) {
                log.error("银联支付回调, 未找到订单信息, orderNO: {}", map.get("merOrderId"));
                return UnionNotifyResultEnum.FAILED_STATUS.getValue();
            }
            // 返回的订单状态
            String status = (String) map.get("status");
            // 根据是否为退款信息直接返回
            if (UnionTradeStateEnum.TRADE_REFUND.getStatus().equals(status)) {
                log.info("银联退款回调, 暂不处理退款信息: {}", order.getOrderCode());
                return UnionNotifyResultEnum.SUCCESS_STATUS.getValue();
            }
            // 判断支付结果是否处理
            if (OrderStatusEnum.PAY_SUCCESS.getStatus().equals(order.getOrderStatus())) {
                log.info("银联支付回调, 该订单已经处理: {}", order.getOrderCode());
                return UnionNotifyResultEnum.SUCCESS_STATUS.getValue();
            }
            // 验签
            boolean checkFlag = UnionPayUtil.checkSign(commonService.getUnionPayConfigByDept(order.getDeptId()), map);
            if (!checkFlag) {
                log.error("银联支付回调, 验签失败");
                return UnionNotifyResultEnum.FAILED_STATUS.getValue();
            }
            // 新订单和不明确订单直接返回
            if (UnionTradeStateEnum.NEW_ORDER.getStatus().equals(status)
                    || UnionTradeStateEnum.UNKNOWN.getStatus().equals(status)) {
                log.info("新订单和不明确订单, orderNo: {}", order.getOrderCode());
                return UnionNotifyResultEnum.FAILED_STATUS.getValue();
            }
            // 支付时间
            String payTime = (String) map.get("payTime");
            // 修改订单状态
            UnionTradeStateEnum tradeStateEnum = EnumUtils.valueOf(UnionTradeStateEnum.class, status,
                    UnionTradeStateEnum.class.getMethod("getStatus"));
            switch (Objects.requireNonNull(tradeStateEnum)) {
                // 支付成功
                case TRADE_SUCCESS:
                    h5EduBillService.billOrderPaySuccess(order.getOrderCode(), LocalDateTimeUtil.parse(payTime, DatePattern.NORM_DATETIME_PATTERN));
                    break;
                // 订单关闭
                case TRADE_CLOSED:
                    order.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
                    order.setPayStatus(PayStatusEnum.CANCEL.getStatus());
                    updateById(order);
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error("处理银联支付回调数据失败", e);
            return UnionNotifyResultEnum.FAILED_STATUS.getValue();
        }
        return UnionNotifyResultEnum.SUCCESS_STATUS.getValue();
    }

    @Override
    public BigDecimal sumDeptPayOrderPrice(Long billId, Long deptId, Integer type) {
        return baseMapper.sumDeptPayOrderPrice(billId, deptId, type);
    }
}
