package com.izouma.jiashanxia.service;

import cn.hutool.core.util.ObjectUtil;
import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
import com.github.binarywang.wxpay.bean.entpay.EntPayResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.izouma.jiashanxia.domain.*;
import com.izouma.jiashanxia.dto.GoodsDTO;
import com.izouma.jiashanxia.dto.PageQuery;
import com.izouma.jiashanxia.enums.FlowType;
import com.izouma.jiashanxia.enums.OrderInfoStatus;
import com.izouma.jiashanxia.enums.RefundStatus;
import com.izouma.jiashanxia.exception.BusinessException;
import com.izouma.jiashanxia.repo.*;
import com.izouma.jiashanxia.utils.JpaUtils;
import com.izouma.jiashanxia.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Slf4j
public class OrderRefundService {

    private final OrderRefundRepo         orderRefundRepo;
    private final OrderInfoRepo           orderInfoRepo;
    private final WxFeeRepo               wxFeeRepo;
    private final WxPayService            wxPayService;
    private final Environment             env;
    private final CommissionRecordService commissionRecordService;
    private final UserPackageRepo         userPackageRepo;
    private final UserPackageFlowRepo     userPackageFlowRepo;

    public Page<OrderRefund> all(PageQuery pageQuery) {
        return orderRefundRepo.findAll(JpaUtils.toSpecification(pageQuery, OrderRefund.class), JpaUtils.toPageRequest(pageQuery));
    }

    /*
    申请退款
     */
    public OrderRefund apply(Long orderId) {
        OrderInfo orderInfo = orderInfoRepo.findById(orderId).orElseThrow(new BusinessException("无订单"));
        switch (orderInfo.getStatus()) {
            case UNPAID:
                throw new BusinessException("暂未支付");
            case CANCELLED:
                throw new BusinessException("已取消");
            case REFUNDED:
                throw new BusinessException("已退款");
            case REQUEST_REFUND:
                throw new BusinessException("申请中");
            case USED:
                throw new BusinessException("已使用无法退款");
        }
//        if (orderInfo.isUse()) {
//            throw new BusinessException("已使用无法退款");
//        }


        // 可多次使用
        if (orderInfo.isRepeatedly()) {
            throw new BusinessException("此套餐无法退款");
//            UserPackageFlow flow = userPackageFlowRepo.findByOrderInfoId(orderId);
//            List<GoodsDTO> content = flow.getContent();
//            Map<Long, UserPackage> userPackageMap = userPackageRepo.findAllByUserId(orderInfo.getUserId())
//                    .stream()
//                    .collect(Collectors.toMap(UserPackage::getGoodsInfoId, aPackage -> aPackage));
//            for (GoodsDTO goods : content) {
//                UserPackage userPackage = userPackageMap.get(goods.getGoodsInfoId());
//                if (ObjectUtil.isNull(userPackage)) {
//                    throw new BusinessException("已使用无法退款");
//                }
//                if (userPackage.getNum() < goods.getNum()) {
//                    throw new BusinessException("已使用无法退款");
//                }
//                // 先扣款
//                userPackage.setNum(userPackage.getNum() - goods.getNum());
//                userPackageRepo.save(userPackage);
//            }
        }

        orderInfo.setStatus(OrderInfoStatus.REQUEST_REFUND);

        OrderRefund refund = OrderRefund.builder()
                .orderInfoId(orderId)
                .applyTime(LocalDateTime.now())
                .status(RefundStatus.PENDING)
                .build();

        orderInfoRepo.save(orderInfo);
        return orderRefundRepo.save(refund);
    }


    /*
    取消退款
    */
    public void cancel(Long orderId) {
        // 恢复订单状态
        OrderInfo orderInfo = orderInfoRepo.findById(orderId).orElseThrow(new BusinessException("无订单"));
        orderInfo.setStatus(OrderInfoStatus.PAID);
        orderInfoRepo.save(orderInfo);

        // 修改退款状态
        OrderRefund OrderRefund = orderRefundRepo.findByOrderInfoIdAndStatus(orderId, RefundStatus.PENDING)
                .orElseThrow(new BusinessException("未申请退款"));
        OrderRefund.setStatus(RefundStatus.CANCEL);
        orderRefundRepo.save(OrderRefund);
        if (orderInfo.isRepeatedly()) {
            this.restore(orderInfo.getUserId(), orderId);
        }
    }

    /**
     * 审核退款
     *
     * @param orderRefundId 申请退款ID
     * @param pass          是否通过
     */
    @Transactional
    public void audit(Long orderRefundId, boolean pass) {
        // 退款记录
        OrderRefund refund = orderRefundRepo.findById(orderRefundId).orElseThrow(new BusinessException("无记录"));
        if (refund.getStatus() != RefundStatus.PENDING) {
            return;
        }
        // 审核时间
        refund.setAuditTime(LocalDateTime.now());

        // 订单记录
        OrderInfo orderInfo = orderInfoRepo.findById(refund.getOrderInfoId()).orElseThrow(new BusinessException("无订单"));

        if (pass) {
            String refundId = String.valueOf(new SnowflakeIdWorker(1, 1).nextId());
            refund.setStatus(RefundStatus.REFUNDING);
//            if (StringUtils.isEmpty(refund.getRefundId())) {
            refund.setRefundId(refundId);
//            }
            orderRefundRepo.save(refund);
            // 退款
            payRefund(orderInfo, refundId);
            return;
        }
        refund.setStatus(RefundStatus.DENY);
        orderInfo.setStatus(OrderInfoStatus.PAID);
        orderInfoRepo.save(orderInfo);
        orderRefundRepo.save(refund);
        if (orderInfo.isRepeatedly()) {
            this.restore(orderInfo.getUserId(), refund.getOrderInfoId());
        }
    }

    /*
    微信退款
     */
    private void payRefund(OrderInfo orderInfo, String refundId) {
        try {
            WxPayRefundRequest req = WxPayRefundRequest.newBuilder()
                    .transactionId(orderInfo.getTransactionId())
                    .outRefundNo(refundId)
                    .totalFee(orderInfo.getPrice().multiply(BigDecimal.valueOf(100)).intValue())
                    .refundFee(orderInfo.getPrice().multiply(BigDecimal.valueOf(100)).intValue())
                    .notifyUrl(env.getProperty("wx.pay.refundNotifyUrl"))
                    .build();

            wxPayService.refund(req);
        } catch (WxPayException e) {
            log.error("发起微信退款", e);
            throw new BusinessException("操作失败，请稍后再试", e.getMessage());
        }
    }

    public void handleRefundNotify(WxPayRefundNotifyResult notifyResult) {
        OrderInfo orderInfo = orderInfoRepo.findByTransactionId(notifyResult.getReqInfo().getTransactionId());

        // 微信退款流水
        String refundId = notifyResult.getReqInfo().getRefundId();
        if (!wxFeeRepo.findByTransactionId(refundId).isPresent()) {
            WxFee wxFee = WxFee.builder()
                    .amount(BigDecimal.valueOf(notifyResult.getReqInfo().getRefundFee() / 100.0))
//                    .isRefund(true)
                    .action(1)
                    .orderId(orderInfo.getId())
                    .type("refund")
                    .transactionId(refundId)
                    .userId(orderInfo.getUserId())
                    .build();
            wxFeeRepo.save(wxFee);
        }

        // 订单状态
        orderInfo.setStatus(OrderInfoStatus.REFUNDED);
        orderInfoRepo.save(orderInfo);

        // 退款状态
        OrderRefund refund = orderRefundRepo.findByRefundId(refundId);
        refund.setStatus(RefundStatus.SUCCESS);
        refund.setAuditTime(LocalDateTime.now());
        orderRefundRepo.save(refund);
        // 收回分销
        commissionRecordService.refund(orderInfo.getId().toString());

        if (orderInfo.isRepeatedly()) {
            this.saveFlow(orderInfo.getId(), refund.getId());
        }
    }

    /*
    保存套餐流水
     */
    public void saveFlow(Long orderId, Long refundId) {
        // 套餐使用流水
        UserPackageFlow flow = userPackageFlowRepo.findByOrderInfoId(orderId);
        List<GoodsDTO> content = flow.getContent();
        content.forEach(goods -> goods.setNum(-goods.getNum()));
        userPackageFlowRepo.save(UserPackageFlow.builder()
                .type(FlowType.REFUND)
                .userId(orderId)
                .content(content)
                .orderInfoId(refundId)
                .build());
    }

    /*
    恢复
     */
    public void restore(Long userId, Long orderId) {
        UserPackageFlow flow = userPackageFlowRepo.findByOrderInfoId(orderId);
        List<GoodsDTO> content = flow.getContent();
        Map<Long, UserPackage> userPackageMap = userPackageRepo.findAllByUserId(userId)
                .stream()
                .collect(Collectors.toMap(UserPackage::getGoodsInfoId, aPackage -> aPackage));
        content.forEach(goods -> {
            UserPackage userPackage = userPackageMap.get(goods.getGoodsInfoId());
            if (ObjectUtil.isNotEmpty(userPackage)) {
                userPackage.setNum(userPackage.getNum() + goods.getNum());
            } else {
                userPackage = UserPackage.builder()
                        .userId(userId)
                        .goodsInfoId(goods.getGoodsInfoId())
                        .num(goods.getNum())
                        .build();
            }
            userPackageRepo.save(userPackage);
        });
    }

}
