package cn.iocoder.yudao.module.member.service.amount;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoCardVO;
import cn.iocoder.yudao.module.member.dal.dataobject.card.CardDO;
import cn.iocoder.yudao.module.member.dal.dataobject.money.MoneyDO;
import cn.iocoder.yudao.module.member.dal.dataobject.order.DishOrderDO;
import cn.iocoder.yudao.module.member.dal.dataobject.rechargelog.RechargeLogDO;
import cn.iocoder.yudao.module.member.dal.dataobject.refund.RefundDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.card.CardMapper;
import cn.iocoder.yudao.module.member.dal.mysql.order.DishOrderMapper;
import cn.iocoder.yudao.module.member.dal.mysql.rechargelog.RechargeLogMapper;
import cn.iocoder.yudao.module.member.dal.mysql.refund.IntegralRefundMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.member.enums.CostTypeEnum;
import cn.iocoder.yudao.module.member.service.money.MoneyService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.module.member.service.amount.LockManager.getUserLock;


/**
 * 微信充值 Service 实现类
 *
 * @author 开发账号
 */
@Service
@Slf4j
public class WxRechargeServiceImpl implements WxRechargeService {
    @Resource
    private MemberUserMapper userMapper;
    @Resource
    private DishOrderMapper dishOrderMapper;
    @Resource
    private CardMapper cardMapper;
    @Resource
    private IntegralRefundMapper refundMapper;
    @Resource
    private RechargeLogMapper rechargeLogMapper;
    @Resource
    private MoneyService moneyService;
    @Resource
    private StringRedisTemplate wxRechargeRedisTemplate;


    @Override
    public void wxRecharge(BigDecimal money, BigDecimal wxAmount, BigDecimal giftAmount, Long userId) {


        BigDecimal wxNewMoney;
        BigDecimal cashNewMoney;
        BigDecimal newMoney;
        String name;

        synchronized (getUserLock(userId)) {
            //获取最新余额
            MemberUserDO memberUserDO = userMapper.selectById(userId);

            BigDecimal wxOldMoney = memberUserDO.getWxAmount();
            BigDecimal oldMoney = memberUserDO.getMoney();
            newMoney = oldMoney.add(money);
            BigDecimal computeMoney = money;
            memberUserDO.setMoney(newMoney);
            if( newMoney.compareTo(BigDecimal.ZERO) >= 0){
                if(oldMoney.compareTo(BigDecimal.ZERO) < 0){
                    memberUserDO.setWxAmount(newMoney);
                    //处理未完全支付订单
                    handleOrderWx(userId, money, wxAmount, giftAmount,null);
                }else {
                    memberUserDO.setWxAmount(wxOldMoney.add(money));
                }
            }

            userMapper.updateById(memberUserDO);

            wxNewMoney = memberUserDO.getWxAmount();
            name = memberUserDO.getNickname();
            cashNewMoney = memberUserDO.getCashAmount();
        }


        //明细处理
        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setFlag(CardDO.ADD);
        cardDO.setChangeMoney(money);
        cardDO.setType(CostTypeEnum.WX_PAY.getCode());
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setCashAmount(cashNewMoney);
        cardDO.setMoney(newMoney);
        cardMapper.insert(cardDO);

        
        //更新缓存
        String redisKey = "USER_" + userId;
        AppUserInfoCardVO appUserInfoCardVO = new AppUserInfoCardVO();
        appUserInfoCardVO.setName(name);
        appUserInfoCardVO.setMoney(newMoney);
        wxRechargeRedisTemplate.opsForValue().set(redisKey, JSONUtil.toJsonStr(appUserInfoCardVO));

    }

    void handleOrderWx(Long userId, BigDecimal money, BigDecimal wxMoney, BigDecimal giftMoney,Long storeId) {

        //开始的微信金额
        BigDecimal wxStart = wxMoney;
        //已审核的订单
        List<Long> refundOrder = getRefundOrder();

        //查询出所有未完全支付订单
        List<DishOrderDO> dishOrderDOS = dishOrderMapper.selectList(Wrappers.<DishOrderDO>lambdaQuery()
                .eq(DishOrderDO::getUserId, userId).eq(DishOrderDO::getOrderStatus, DishOrderDO.TOCOMPLETE)
                .eq(storeId != null ,DishOrderDO::getStoreId,storeId));

        ArrayList<DishOrderDO> updateList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(dishOrderDOS)) {
            for (DishOrderDO dishOrderDO : dishOrderDOS) {
                BigDecimal payAmount = dishOrderDO.getPayAmount();
                if (payAmount.compareTo(money) <= 0) {
                    dishOrderDO.setOrderStatus(DishOrderDO.COMPLETE);
                    dishOrderDO.setPayAmount(BigDecimal.ZERO);
                    dishOrderDO.setRefundAmount(dishOrderDO.getRefundAmount().add(payAmount));
                    if (refundOrder.contains(dishOrderDO.getId())) {
                        dishOrderDO.setRefundAmount(BigDecimal.ZERO);
                    }
                    if (payAmount.compareTo(wxMoney) <= 0) {
                        dishOrderDO.setWxAmount(dishOrderDO.getWxAmount().add(payAmount));
                        wxMoney = wxMoney.subtract(payAmount);
                    } else {
                        dishOrderDO.setWxAmount(dishOrderDO.getWxAmount().add(wxMoney));
                        wxMoney = BigDecimal.ZERO;
                        BigDecimal left = payAmount.subtract(wxMoney);
                        dishOrderDO.setGiftAmount(dishOrderDO.getGiftAmount().add(left));
                        giftMoney = giftMoney.subtract(left);
                    }
                    money = money.subtract(payAmount);

                    updateList.add(dishOrderDO);
                } else {
                    dishOrderDO.setPayAmount(payAmount.subtract(money));
                    dishOrderDO.setRefundAmount(dishOrderDO.getRefundAmount().add(money));
                    if (refundOrder.contains(dishOrderDO.getId())) {
                        dishOrderDO.setRefundAmount(BigDecimal.ZERO);
                    }
                    dishOrderDO.setWxAmount(dishOrderDO.getWxAmount().add(wxMoney));
                    dishOrderDO.setGiftAmount(dishOrderDO.getGiftAmount().add(giftMoney));
                    wxMoney = BigDecimal.ZERO;
                    updateList.add(dishOrderDO);
                    break;
                }
            }
        }

        rechargeLog(wxStart.subtract(wxMoney));
        if (CollectionUtil.isNotEmpty(updateList)) {
            dishOrderMapper.updateBatch(updateList);
        }

    }

    //补充金额记录
    public void rechargeLog(BigDecimal money) {
        List<RechargeLogDO> rechargeLogDOList = rechargeLogMapper.selectList(Wrappers.<RechargeLogDO>lambdaQuery()
                .apply("TO_DAYS(NOW())-TO_DAYS(create_time) = 0"));
        if (CollectionUtil.isNotEmpty(rechargeLogDOList)) {
            RechargeLogDO rechargeLogDO = rechargeLogDOList.get(0);
            rechargeLogDO.setMoney(rechargeLogDO.getMoney().add(money));
            rechargeLogMapper.updateById(rechargeLogDO);
        } else {
            RechargeLogDO rechargeLogDO = new RechargeLogDO();
            rechargeLogDO.setMoney(money);
            rechargeLogDO.setCreateTime(LocalDateTime.now());
            rechargeLogMapper.insert(rechargeLogDO);
        }
    }

    public List<Long> getRefundOrder() {
        List<RefundDO> refundDOS = refundMapper.selectList(Wrappers.<RefundDO>lambdaQuery().in(RefundDO::getStatus, Arrays.asList("2", "3")));
        ArrayList<Long> orderIds = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(refundDOS)) {
            List<Long> collect = refundDOS.stream().map(RefundDO::getOrderId).collect(Collectors.toList());
            orderIds.addAll(collect);
        }

        return orderIds;
    }


}
