package com.yonge.cooleshow.biz.dal.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yonge.cooleshow.biz.dal.dao.ActivityRewardChangeStockDao;
import com.yonge.cooleshow.biz.dal.dto.ActivityPlanRewardDto;
import com.yonge.cooleshow.biz.dal.entity.ActivityRewardChangeStock;
import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
import com.yonge.cooleshow.biz.dal.service.ActivityRewardService;
import com.yonge.cooleshow.biz.dal.vo.ActivityRewardVo;
import com.yonge.cooleshow.common.enums.ActivityTypeEnum;
import com.yonge.cooleshow.common.enums.CacheNameEnum;
import com.yonge.cooleshow.common.enums.RewardTypeEnum;
import com.yonge.toolset.base.exception.BizException;
import com.yonge.toolset.payment.util.DistributedLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.yonge.cooleshow.biz.dal.entity.ActivityPlanReward;
import com.yonge.cooleshow.biz.dal.vo.ActivityPlanRewardVo;
import com.yonge.cooleshow.biz.dal.dto.search.ActivityPlanRewardSearch;
import com.yonge.cooleshow.biz.dal.dao.ActivityPlanRewardDao;
import com.yonge.cooleshow.biz.dal.service.ActivityPlanRewardService;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;


@Service
public class ActivityPlanRewardServiceImpl extends ServiceImpl<ActivityPlanRewardDao, ActivityPlanReward> implements ActivityPlanRewardService {
    private final static Logger log = LoggerFactory.getLogger(ActivityPlanRewardServiceImpl.class);

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ActivityRewardService activityRewardService;

    @Resource
    private ActivityRewardChangeStockDao changeStockDao;

    @Override
    public ActivityPlanRewardVo detail(Long id) {
        return baseMapper.detail(id);
    }

    @Override
    public ActivityPlanReward detailByActivityIdAndRewardId(Long activityId, Long rewardId, String group) {
        return baseMapper.detailByActivityIdAndRewardId(activityId, rewardId, group);
    }

    @Override
    public IPage<ActivityPlanRewardVo> selectPage(IPage<ActivityPlanRewardVo> page, ActivityPlanRewardSearch query) {
        return page.setRecords(baseMapper.selectPage(page, query));
    }

    @Override
    public List<ActivityPlanRewardDto> queryActivityPlanReward(Long activityId) {
        return baseMapper.queryActivityPlanReward(activityId);
    }

    @Override
    public List<ActivityPlanReward> getDelRewardList(Long activityId, List<Long> rewardIds) {
        return baseMapper.getDelRewardList(activityId, rewardIds);
    }

    @Override
    public Boolean saveOrUpdateReward(ActivityPlanRewardDto rewardDto) {
        DistributedLock.of(redissonClient)
                .runIfLockToFunction(CacheNameEnum.LOCK_ACTIVITY_REWARD_STOCK.getRedisKey(rewardDto.getId())
                        , (out) -> {
                            DistributedLock.of(redissonClient)
                                    .runIfLockToFunction(CacheNameEnum.LOCK_REWARD_STOCK.getRedisKey(out.getId())
                                            , (param) -> {
                                                ActivityRewardVo detail = activityRewardService.detail(param.getRewardId());

                                                ActivityPlanReward old = detailByActivityIdAndRewardId(param.getActivityId(), param.getRewardId(), param.getGroup());
                                                if (null == old) {
                                                    Integer stock = detail.getStock() - param.getRewardNum();
                                                    if (stock < 0) {
                                                        //库存不足，使用全部库存
                                                        stock = 0;
                                                        param.setRewardNum(detail.getStock());
                                                    }

                                                    save(param);

                                                    //入库存变更记录
                                                    ActivityRewardChangeStock changeStock = new ActivityRewardChangeStock();
                                                    changeStock.setRewardId(param.getRewardId());
                                                    changeStock.setStock(param.getRewardNum());
                                                    changeStock.setChangeType(InOrOutEnum.OUT);
                                                    changeStock.setSource("ACTIVITY");
                                                    changeStockDao.insert(changeStock);

                                                    detail.setStock(stock);
                                                    activityRewardService.updateById(detail);
                                                } else {
                                                    param.setId(old.getId());

                                                    //新增数（能为负）
                                                    Integer changeStock = param.getRewardNum() - old.getRewardNum();
                                                    if (changeStock == 0) {
                                                        return true;
                                                    }

                                                    //调整后的库存
                                                    Integer stock = detail.getStock() - changeStock;
                                                    if (stock < 0) {
                                                        //库存不足，使用全部库存
                                                        stock = 0;
                                                        changeStock = detail.getStock();
                                                    }

                                                    param.setRewardNum(old.getRewardNum() + changeStock);
                                                    updateById(param);
                                                    //入库存变更记录
                                                    ActivityRewardChangeStock changeStockObj = new ActivityRewardChangeStock();
                                                    changeStockObj.setRewardId(param.getRewardId());
                                                    changeStockObj.setStock(Math.abs(changeStock));
                                                    changeStockObj.setChangeType(
                                                            changeStock < 0 ? InOrOutEnum.IN : InOrOutEnum.OUT);
                                                    changeStockObj.setSource("ACTIVITY");
                                                    changeStockDao.insert(changeStockObj);

                                                    detail.setStock(stock);
                                                    activityRewardService.updateById(detail);
                                                }
                                                return true;
                                            }, out, 10l);
                            return true;
                        }, rewardDto, 10l);
        return true;
    }

    @Override
    public Boolean recoveryReward(Long activityId) {
        List<ActivityPlanReward> list = list(Wrappers.<ActivityPlanReward>lambdaQuery()
                .eq(ActivityPlanReward::getActivityId, activityId)
                .gt(ActivityPlanReward::getRewardNum, 0));
        for (ActivityPlanReward activityPlanReward : list) {
            recoveryReward(activityPlanReward);
        }
        return true;
    }

    @Override
    public Boolean recoveryReward(ActivityPlanReward reward) {
        DistributedLock.of(redissonClient)
                .runIfLockToFunction(CacheNameEnum.LOCK_ACTIVITY_REWARD_STOCK.getRedisKey(reward.getId())
                        , (out) -> {
                            DistributedLock.of(redissonClient)
                                    .runIfLockToFunction(CacheNameEnum.LOCK_REWARD_STOCK.getRedisKey(out.getId())
                                            , (param) -> {
                                                ActivityRewardVo detail = activityRewardService.detail(param.getRewardId());

                                                ActivityPlanReward old = detailByActivityIdAndRewardId(param.getActivityId(), param.getRewardId(), reward.getGroup());

                                                if (old.getRewardNum() > 0) {
                                                    //入库存变更记录
                                                    ActivityRewardChangeStock changeStockObj = new ActivityRewardChangeStock();
                                                    changeStockObj.setRewardId(param.getRewardId());
                                                    changeStockObj.setStock(param.getRewardNum());
                                                    changeStockObj.setChangeType(InOrOutEnum.IN);
                                                    changeStockObj.setSource("ACTIVITY");
                                                    changeStockDao.insert(changeStockObj);

                                                    detail.setStock(detail.getStock() + old.getRewardNum());
                                                    activityRewardService.updateById(detail);

                                                    old.setRewardNum(0);
                                                    updateById(old);
                                                }
                                                return true;
                                            }, out, 10l);
                            return true;
                        }, reward, 10l);
        return false;
    }

    @Override
    public int reduceStock(Long activityId, Long activityRewardId) {
        return baseMapper.reduceStock(activityId, activityRewardId);
    }

}