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


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.module.member.controller.admin.business.vo.StatisticsVo;
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.user.MemberUserDO;
import cn.iocoder.yudao.module.member.enums.CostTypeEnum;
import cn.iocoder.yudao.module.member.enums.TimePeriodEnum;
import cn.iocoder.yudao.module.member.service.business.BusinessService;
import cn.iocoder.yudao.module.member.service.card.CardService;
import cn.iocoder.yudao.module.member.service.group.MemberGroupService;
import cn.iocoder.yudao.module.member.service.money.MoneyService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import com.sun.istack.NotNull;
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.Arrays;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.CASH_AMOUNT_NOT_ENOUGH;
import static cn.iocoder.yudao.module.member.service.amount.LockManager.getUserLock;


/**
 * 餐盘统一扣款 Service 实现类
 *
 * @author 开发账号
 */

@Service
@Slf4j
public class DeductionServiceImpl implements DeductionService {

    @Resource
    private StringRedisTemplate deductionRedisTemplate;
    @Resource
    private CardService cardService;
    @Resource
    private MemberUserService userService;
    @Resource
    private BusinessService businessService;
    @Resource
    private MoneyService moneyService;


    private static final List<String> CHECK_LIST = Arrays.asList(CostTypeEnum.SHOPPING.getCode());

    @Override
    public void deduction(DishOrderDO dishOrderDO) {
        Long userId = dishOrderDO.getUserId();

        BigDecimal changeMoney;
        BigDecimal wxNewMoney;
        BigDecimal giftNewMoney;
        BigDecimal cashNewMoney;

        String name;

        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            MoneyDO moneyDO = moneyService.getMoney(dishOrderDO.getUserId(), dishOrderDO.getStoreId());
            //新的总价
            String s = deductionRedisTemplate.opsForValue().get(dishOrderDO.getDiningPlatesNum() + "-" + dishOrderDO.getStoreId());
            BigDecimal total = new BigDecimal(StrUtil.isBlank(s) ? "0" : s);
            //现有金额
            //BigDecimal money = user.getMoney();
            //计算减免价格
            BigDecimal reductionAmount = BigDecimal.ZERO;
            //补贴
            BigDecimal subsidyAmount = BigDecimal.ZERO;
            if (total.compareTo(BigDecimal.ZERO) > 0) {
                subsidyAmount = userService.getReductionAmount(userId, total, dishOrderDO.getCreateTime());
            }
            dishOrderDO.setSubsidyAmount(subsidyAmount);
            if (dishOrderDO.getReductionAmount().compareTo(BigDecimal.ZERO) > 0) {
                reductionAmount = reductionAmount.add(dishOrderDO.getReductionAmount());
                dishOrderDO.setReductionAmount(reductionAmount);
            } else {
                dishOrderDO.setReductionAmount(reductionAmount);
            }
            dishOrderDO.setReductionState(reductionAmount.compareTo(BigDecimal.ZERO) > 0 ? "1" : "0");
            //计算减免后的总价
            reductionAmount = reductionAmount.add(subsidyAmount);
            if (total.compareTo(reductionAmount) < 0) {
                total = BigDecimal.ZERO;
            } else {
                total = total.subtract(reductionAmount);
            }
            //当前金额
            BigDecimal wxAmount = user.getWxAmount();
            BigDecimal giftAmount = user.getGiftAmount();
            BigDecimal cashAmount = BigDecimal.ZERO;
            if(ObjectUtil.isNotEmpty(moneyDO)){
                cashAmount = moneyDO.getCashAmount();
            }

            BigDecimal money = wxAmount.add(giftAmount).add(cashAmount);
            // user.setMoney(money.subtract(total));
            //待支付金额 最大退款金额
            dishOrderDO.setRefundAmount(total);
            if (money.compareTo(BigDecimal.ZERO) <= 0) {
                dishOrderDO.setPayAmount(total);
                dishOrderDO.setRefundAmount(BigDecimal.ZERO);
            } else {
                if (total.compareTo(money) > 0) {
                    dishOrderDO.setPayAmount(total.subtract(money));
                    dishOrderDO.setRefundAmount(money);
                }
            }

            if (total.compareTo(money) > 0) {
                dishOrderDO.setOrderStatus(DishOrderDO.TOCOMPLETE);
                dishOrderDO.setWxAmount(wxAmount);
                dishOrderDO.setCashAmount(cashAmount);
                dishOrderDO.setGiftAmount(giftAmount);

                user.setWxAmount(BigDecimal.ZERO);
                moneyDO.setCashAmount(BigDecimal.ZERO);
                user.setGiftAmount(BigDecimal.ZERO);
                moneyDO.setDebtAmount(money.subtract(total).add(moneyDO.getDebtAmount()));
            } else {
                dishOrderDO.setOrderStatus(DishOrderDO.COMPLETE);
                //计算金额
                if (total.compareTo(cashAmount) <= 0) {
                    moneyDO.setCashAmount(cashAmount.subtract(total));
                    dishOrderDO.setCashAmount(total);
                } else {
                    moneyDO.setCashAmount(BigDecimal.ZERO);
                    dishOrderDO.setCashAmount(cashAmount);
                    BigDecimal total1 = total.subtract(cashAmount);
                    if (total1.compareTo(giftAmount) <= 0) {
                        user.setGiftAmount(giftAmount.subtract(total1));
                        dishOrderDO.setGiftAmount(total1);
                    } else {
                        user.setGiftAmount(BigDecimal.ZERO);
                        dishOrderDO.setGiftAmount(giftAmount);
                        BigDecimal total2 = total1.subtract(giftAmount);

                        user.setWxAmount(wxAmount.subtract(total2));
                        dishOrderDO.setWxAmount(total2);
                    }
                }
            }
            //修改
            userService.updateById(user);
            moneyService.updateById(moneyDO);

            wxNewMoney = user.getWxAmount();
            giftNewMoney = user.getGiftAmount();
            cashNewMoney = moneyDO.getCashAmount();
            changeMoney = total;
            name = user.getNickname();
        }

        //记录消费记录
        CardDO cardDO = new CardDO();
        cardDO.setType(TimePeriodEnum.getTimePeriod(dishOrderDO.getCreateTime()));
        cardDO.setUserId(userId);
        cardDO.setChangeMoney(changeMoney);
        cardDO.setFlag(CardDO.MINUS);
        cardDO.setCashAmount(cashNewMoney);
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setGiftAmount(giftNewMoney);
        cardDO.setCarteenId(dishOrderDO.getStoreId());

        cardService.insertOne(cardDO);

        dishOrderDO.setTotalMoney(changeMoney);
        dishOrderDO.setUpdateTime(LocalDateTime.now());
        deductionRedisTemplate.delete(dishOrderDO.getDiningPlatesNum() + "-" + dishOrderDO.getStoreId());

        //更新营业数据
        if (dishOrderDO.getOrderStatus().equals(DishOrderDO.COMPLETE)) {
            StatisticsVo statisticsVo = new StatisticsVo();
            statisticsVo.setCarteenId(dishOrderDO.getStoreId());
            statisticsVo.setTotalMoney(dishOrderDO.getTotalMoney());
            statisticsVo.setOrderSum(1);
            statisticsVo.setReduceMoney(dishOrderDO.getReductionAmount());
            statisticsVo.setTime(dishOrderDO.getCreateTime());
            statisticsVo.setOrderId(dishOrderDO.getId());
            businessService.updateStatistics(statisticsVo);
        }

        //更新redis
        updateRedis(userId, name, wxNewMoney);
    }


    @Override
    public void cashDraw(Long userId, BigDecimal money, String type, Long storeId) {

        BigDecimal wxNewMoney;
        BigDecimal giftNewMoney;
        BigDecimal cashNewMoney;
        String name;

        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            MoneyDO moneyDO = moneyService.getMoney(userId, storeId);
            if (ObjectUtil.isEmpty(moneyDO) || moneyDO.getCashAmount().compareTo(BigDecimal.ZERO) < 1
                    || money.compareTo(moneyDO.getCashAmount()) > 0) {
                throw exception(CASH_AMOUNT_NOT_ENOUGH);
            }
            moneyDO.setCashAmount(moneyDO.getCashAmount().subtract(money));
            moneyService.updateById(moneyDO);

            wxNewMoney = user.getWxAmount();
            giftNewMoney = user.getGiftAmount();
            cashNewMoney = moneyDO.getCashAmount();
            name = user.getNickname();
        }

        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setFlag(CardDO.MINUS);
        cardDO.setChangeMoney(money);
        cardDO.setType(type);
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setGiftAmount(giftNewMoney);
        cardDO.setCashAmount(cashNewMoney);
        cardDO.setCarteenId(storeId);
        cardService.insertOne(cardDO);

        //更新redis
        updateRedis(userId, name, wxNewMoney);
    }


    @Override
    public void wxDraw(Integer amount, Long userId) {

        BigDecimal wxNewMoney;
        BigDecimal giftNewMoney;
        BigDecimal cashNewMoney;
        String name;
        BigDecimal changeMoney = new BigDecimal(amount).divide(new BigDecimal("100"));

        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            user.setWxAmount(user.getWxAmount().subtract(changeMoney));
            userService.updateById(user);
            wxNewMoney = user.getWxAmount();
            giftNewMoney = user.getGiftAmount();
            cashNewMoney = user.getCashAmount();
            name = user.getNickname();
        }


        //获取最新余额
        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setFlag(CardDO.MINUS);
        cardDO.setChangeMoney(changeMoney);
        cardDO.setType("7");
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setGiftAmount(giftNewMoney);
        cardDO.setCashAmount(cashNewMoney);
        cardService.insertOne(cardDO);

        //更新redis
        updateRedis(userId, name, wxNewMoney);
    }

    @Override
    public void refund(Long userId, BigDecimal money, DishOrderDO dishOrderDO) {
        //获取最新余额
        BigDecimal wxNewMoney;
        BigDecimal giftNewMoney;
        BigDecimal cashNewMoney;
        String name;

        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            MoneyDO moneyDO = moneyService.getMoney(userId, dishOrderDO.getStoreId());

            //用户当前金额
            BigDecimal wxAmount = user.getWxAmount();
            BigDecimal giftAmount = user.getGiftAmount();
            BigDecimal cashAmount = moneyDO.getCashAmount();
            BigDecimal debtAmount = moneyDO.getDebtAmount();


            //订单消费金额
            BigDecimal dishWxAmount = dishOrderDO.getWxAmount();
            BigDecimal dishCashAmount = dishOrderDO.getCashAmount();
            BigDecimal dishGiftAmount = dishOrderDO.getGiftAmount();

            BigDecimal newMoney = money;

            if (debtAmount.compareTo(BigDecimal.ZERO) > 0) {
                newMoney = debtAmount.add(newMoney);
            }

            if (newMoney.compareTo(BigDecimal.ZERO) < 0) {
                user.setWxAmount(BigDecimal.ZERO);
                user.setGiftAmount(BigDecimal.ZERO);
                user.setCashAmount(BigDecimal.ZERO);
            } else {
                if (newMoney.compareTo(dishGiftAmount) <= 0) {
                    user.setGiftAmount(giftAmount.add(newMoney));
                } else {
                    user.setGiftAmount(giftAmount.add(dishGiftAmount));
                    BigDecimal money1 = newMoney.subtract(dishGiftAmount);

                    if (money1.compareTo(dishCashAmount) <= 0) {
                        moneyDO.setCashAmount(cashAmount.add(money1));
                    } else {
                        moneyDO.setCashAmount(cashAmount.add(dishCashAmount));
                        BigDecimal money2 = money1.subtract(dishCashAmount);
                        if (money2.compareTo(dishWxAmount) <= 0) {
                            user.setWxAmount(wxAmount.add(money2));
                        } else {
                            user.setWxAmount(wxAmount.add(dishWxAmount));
                        }

                    }
                }
            }
            userService.updateById(user);
            moneyService.updateById(moneyDO);
            wxNewMoney = user.getWxAmount();
            giftNewMoney = user.getGiftAmount();
            cashNewMoney = moneyDO.getCashAmount();
            name = user.getNickname();
        }

        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setFlag(CardDO.ADD);
        cardDO.setChangeMoney(money);
        cardDO.setType(CostTypeEnum.REFUND.getCode());
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setGiftAmount(giftNewMoney);
        cardDO.setCashAmount(cashNewMoney);
        cardDO.setCarteenId(dishOrderDO.getStoreId());
        cardService.insertOne(cardDO);

        //更新redis
        updateRedis(userId, name, wxNewMoney);
    }

    @Override
    public void reduction(Long userId, BigDecimal money, BigDecimal giftAmount, BigDecimal cashAmount, BigDecimal wxAmount, Long storeId) {

        BigDecimal giftNewAmount;
        BigDecimal cashNewAmount;
        BigDecimal wxNewAmount;
        String name;

        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            MoneyDO moneyDO = moneyService.getMoney(userId, storeId);

            moneyDO.setCashAmount(moneyDO.getCashAmount().add(cashAmount));
            user.setGiftAmount(user.getGiftAmount().add(giftAmount));
            user.setWxAmount(user.getWxAmount().add(wxAmount));
            userService.updateById(user);
            moneyService.updateById(moneyDO);

            wxNewAmount = user.getWxAmount();
            giftNewAmount = user.getGiftAmount();
            cashNewAmount = moneyDO.getCashAmount();
            name = user.getNickname();
        }
        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setFlag(CardDO.ADD);
        cardDO.setChangeMoney(money);
        cardDO.setType(CostTypeEnum.REFUND.getCode());
        cardDO.setWxAmount(wxNewAmount);
        cardDO.setCashAmount(cashNewAmount);
        cardDO.setGiftAmount(giftNewAmount);
        cardDO.setCarteenId(storeId);
        cardService.insertOne(cardDO);

        //更新redis
        updateRedis(userId, name, wxNewAmount);
    }

    @Override
    public BigDecimal storeDeduction(BigDecimal total, @NotNull Long userId, Long storeId) {

        BigDecimal wxNewMoney;
        BigDecimal giftNewMoney;
        BigDecimal cashNewMoney;
        String name;
        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            MoneyDO moneyDO = moneyService.getMoney(userId, storeId);

            BigDecimal wxAmount = user.getWxAmount();
            BigDecimal giftAmount = user.getGiftAmount();
            BigDecimal cashAmount = moneyDO.getCashAmount();

            BigDecimal money = wxAmount.add(giftAmount).add(cashAmount);

            if (total.compareTo(money) > 0) {
                return money.subtract(total);
            }

            //计算金额
            if (total.compareTo(cashAmount) <= 0) {
                moneyDO.setCashAmount(cashAmount.subtract(total));
            } else {
                moneyDO.setCashAmount(BigDecimal.ZERO);
                BigDecimal total1 = total.subtract(cashAmount);
                if (total1.compareTo(giftAmount) <= 0) {
                    user.setGiftAmount(giftAmount.subtract(total1));
                } else {
                    user.setGiftAmount(BigDecimal.ZERO);
                    BigDecimal total2 = total1.subtract(giftAmount);
                    user.setWxAmount(wxAmount.subtract(total2));
                }
            }
            userService.updateById(user);
            moneyService.updateById(moneyDO);

            wxNewMoney = user.getWxAmount();
            giftNewMoney = user.getGiftAmount();
            cashNewMoney = moneyDO.getCashAmount();
            name = user.getNickname();
        }

        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setChangeMoney(total);
        cardDO.setFlag(CardDO.MINUS);
        cardDO.setType(CostTypeEnum.SHOPPING.getCode());
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setCashAmount(cashNewMoney);
        cardDO.setGiftAmount(giftNewMoney);
        cardDO.setCarteenId(storeId);
        cardService.insertOne(cardDO);


        //更新redis
        updateRedis(userId, name, wxNewMoney);

        return wxNewMoney.add(giftNewMoney).add(cashNewMoney);
    }


    public void updateRedis(Long userId, String name, BigDecimal newMoney) {
        //更新缓存
        String redisKey = "USER_" + userId;
        AppUserInfoCardVO appUserInfoCardVO = new AppUserInfoCardVO();
        appUserInfoCardVO.setName(name);
        appUserInfoCardVO.setMoney(newMoney);
        deductionRedisTemplate.opsForValue().set(redisKey, JSONUtil.toJsonStr(appUserInfoCardVO));
    }

    @Override
    public void deduct(Long userId, BigDecimal money, String type,Long storeId) {

        BigDecimal wxNewMoney;
        BigDecimal giftNewMoney;
        BigDecimal cashNewMoney;
        String name;

        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            MoneyDO moneyDO = moneyService.getMoney(userId, storeId);
            BigDecimal total = moneyDO.getCashAmount().add(user.getWxAmount()).add(user.getGiftAmount());
            if (total.compareTo(money) < 0) {
                if (CHECK_LIST.contains(type)) {
                    throw exception(CASH_AMOUNT_NOT_ENOUGH);
                }
                user.setWxAmount(BigDecimal.ZERO);
                moneyDO.setCashAmount(BigDecimal.ZERO);
                user.setGiftAmount(BigDecimal.ZERO);
                moneyDO.setDebtAmount(total.subtract(money));
            } else {
                BigDecimal wxAmount = user.getWxAmount();
                BigDecimal giftAmount = user.getGiftAmount();
                BigDecimal cashAmount = moneyDO.getCashAmount();
                //计算金额
                if (money.compareTo(cashAmount) <= 0) {
                    moneyDO.setCashAmount(cashAmount.subtract(money));
                } else {
                    moneyDO.setCashAmount(BigDecimal.ZERO);
                    BigDecimal total1 = money.subtract(cashAmount);
                    if (total1.compareTo(giftAmount) <= 0) {
                        user.setGiftAmount(giftAmount.subtract(total1));
                    } else {
                        user.setGiftAmount(BigDecimal.ZERO);
                        BigDecimal total2 = total1.subtract(giftAmount);
                        user.setWxAmount(wxAmount.subtract(total2));
                    }
                }
            }
            userService.updateById(user);
            moneyService.updateById(moneyDO);

            wxNewMoney = user.getWxAmount();
            giftNewMoney = user.getGiftAmount();
            cashNewMoney = moneyDO.getCashAmount();
            name = user.getNickname();
        }

        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setFlag(CardDO.MINUS);
        cardDO.setChangeMoney(money);
        cardDO.setType(type);
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setGiftAmount(giftNewMoney);
        cardDO.setCashAmount(cashNewMoney);
        cardDO.setCarteenId(storeId);
        cardService.insertOne(cardDO);

        //更新redis
        updateRedis(userId, name, wxNewMoney);
    }

    @Override
    public void storeOfflineDeduction(BigDecimal total, Long userId, Long storeId) {
        BigDecimal wxNewMoney;
        BigDecimal giftNewMoney;
        BigDecimal cashNewMoney;
        String name;
        synchronized (getUserLock(userId)) {
            MemberUserDO user = userService.getUser(userId);
            MoneyDO moneyDO = moneyService.getMoney(userId, storeId);

            BigDecimal wxAmount = user.getWxAmount();
            BigDecimal giftAmount = user.getGiftAmount();
            BigDecimal cashAmount = moneyDO.getCashAmount();

            //计算金额
            if (total.compareTo(cashAmount) <= 0) {
                moneyDO.setCashAmount(cashAmount.subtract(total));
            } else {
                moneyDO.setCashAmount(BigDecimal.ZERO);
                BigDecimal total1 = total.subtract(cashAmount);
                if (total1.compareTo(giftAmount) <= 0) {
                    user.setGiftAmount(giftAmount.subtract(total1));
                } else {
                    user.setGiftAmount(BigDecimal.ZERO);
                    BigDecimal total2 = total1.subtract(giftAmount);

                    if(total2.compareTo(wxAmount) <= 0) {
                        user.setWxAmount(wxAmount.subtract(total2));
                    }else {
                        user.setWxAmount(BigDecimal.ZERO);
                        moneyDO.setDebtAmount(wxAmount.subtract(total2).add(moneyDO.getDebtAmount()));
                    }
                }
            }
            userService.updateById(user);
            moneyService.updateById(moneyDO);

            wxNewMoney = user.getWxAmount();
            giftNewMoney = user.getGiftAmount();
            cashNewMoney = moneyDO.getCashAmount();
            name = user.getNickname();
        }

        CardDO cardDO = new CardDO();
        cardDO.setUserId(userId);
        cardDO.setChangeMoney(total);
        cardDO.setFlag(CardDO.MINUS);
        cardDO.setType(CostTypeEnum.SHOPPING.getCode());
        cardDO.setWxAmount(wxNewMoney);
        cardDO.setCashAmount(cashNewMoney);
        cardDO.setGiftAmount(giftNewMoney);
        cardDO.setCarteenId(storeId);
        cardService.insertOne(cardDO);


        //更新redis
        updateRedis(userId, name, wxNewMoney);

    }
}

