package com.hzs.activity.draw.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzs.activity.draw.mapper.AcDrawRewardRecordMapper;
import com.hzs.activity.draw.param.AcDrawRecordParam;
import com.hzs.activity.draw.param.DrawGiftConfigParam;
import com.hzs.activity.draw.service.IAcDrawGiftRuleConfigService;
import com.hzs.activity.draw.service.IAcDrawRewardNumService;
import com.hzs.activity.draw.service.IAcDrawRewardRecordService;
import com.hzs.activity.pick.service.IAcPickService;
import com.hzs.activity.pick.service.IAcPickUpConfigService;
import com.hzs.common.core.constant.CacheConstants;
import com.hzs.common.core.enums.EActType;
import com.hzs.common.core.enums.EProductAttrType;
import com.hzs.common.core.service.RedisService;
import com.hzs.common.core.utils.ComputeUtil;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.domain.activity.base.AcPickUpConfig;
import com.hzs.common.domain.activity.base.ext.AcGiftConfigExt;
import com.hzs.common.domain.activity.draw.AcDrawRewardNum;
import com.hzs.common.domain.activity.draw.AcDrawRewardRecord;
import com.hzs.common.domain.activity.draw.ext.AcDrawGiftRuleConfigExt;
import com.hzs.common.domain.activity.draw.ext.AcDrawRewardRecordExt;
import com.hzs.member.base.IMemberServiceApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 抽奖中奖记录 服务实现类
 *
 * @author hzs
 * @since 2023-04-21
 */
@Service
@Slf4j
public class AcDrawRewardRecordServiceImpl extends ServiceImpl<AcDrawRewardRecordMapper, AcDrawRewardRecord> implements IAcDrawRewardRecordService {

    @DubboReference
    IMemberServiceApi memberServiceApi;

    private RedisService redisService;

    private IAcDrawGiftRuleConfigService acDrawGiftConfigService;

    private IAcDrawRewardNumService acDrawRewardNumService;

    private IAcPickUpConfigService acPickUpConfigService;

    /**
     * 提货单
     */
    private IAcPickService acPickService;

    @Autowired
    public void setAcPickService(IAcPickService acPickService) {
        this.acPickService = acPickService;
    }

    @Autowired
    public void setAcPickUpConfigService(IAcPickUpConfigService acPickUpConfigService) {
        this.acPickUpConfigService = acPickUpConfigService;
    }

    @Autowired
    public void setAcDrawRewardNumService(IAcDrawRewardNumService acDrawRewardNumService) {
        this.acDrawRewardNumService = acDrawRewardNumService;
    }

    @Autowired
    public void setAcDrawGiftConfigService(IAcDrawGiftRuleConfigService acDrawGiftConfigService) {
        this.acDrawGiftConfigService = acDrawGiftConfigService;
    }

    @Autowired
    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AcDrawGiftRuleConfigExt obtainAwardRecordByUser(AcDrawRewardNum acDrawRewardNum) {
        Long rewardRecordId = null;
        String key = CacheConstants.DRAW_REWARD_GIFT + acDrawRewardNum.getPkBaseId();
        try {
            // 先扣除数量，加锁
            acDrawRewardNumService.updateAcDrawRewardNum(acDrawRewardNum);
            // 抽奖
            List<Long> rewardRecordList = redisService.getCacheList(key);
            // 奖池少于200的时候重新生成 1轮
            DrawGiftConfigParam drawGiftConfigParam = DrawGiftConfigParam.builder().pkBaseId(acDrawRewardNum.getPkBaseId()).pkCountry(acDrawRewardNum.getPkCountry()).build();
            if (rewardRecordList == null || rewardRecordList.size() < 200) {
                List<AcDrawGiftRuleConfigExt> acDrawGiftRuleConfigList = acDrawGiftConfigService.queryAcDrawGiftConfig(drawGiftConfigParam);
                ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
                Lock lock = readWriteLock.writeLock();
                try {
                    lock.lock();
                    if (rewardRecordList == null || rewardRecordList.size() < 200) {
                        generateAwardRecords(acDrawGiftRuleConfigList, acDrawRewardNum.getPkBaseId());
                    }
                } finally {
                    lock.unlock();
                }
            }
            rewardRecordId = (Long) redisService.leftPopCacheList(key);
            // 奖品配置
            drawGiftConfigParam.setPkId(rewardRecordId);
            List<AcDrawGiftRuleConfigExt> acDrawGiftRuleConfigList = acDrawGiftConfigService.queryAcDrawGiftConfig(drawGiftConfigParam);
            if (acDrawGiftRuleConfigList.size() == 0) {
                throw new RuntimeException();
            }
            // 实体产品进提货
            // 封装中奖记录
            AcDrawRewardRecord acDrawRewardRecord = AcDrawRewardRecord.builder()
                    .pkBaseId(acDrawRewardNum.getPkBaseId())
                    .pkMember(acDrawRewardNum.getPkMember())
                    .pkDrawGiftId(rewardRecordId)
                    .build();
            acDrawRewardRecord.setPkCountry(acDrawRewardNum.getPkCountry());
            acDrawRewardRecord.setPkCreator(acDrawRewardNum.getPkMember());
            // 保存中奖记录
            baseMapper.insert(acDrawRewardRecord);
            boolean giftType = Boolean.FALSE;
            AcDrawGiftRuleConfigExt acDrawGiftRuleConfig = acDrawGiftRuleConfigList.get(0);
            for (AcGiftConfigExt acGiftConfigExt : acDrawGiftRuleConfig.getAcGiftConfigList()) {
                if (EProductAttrType.REAL.getValue() == acGiftConfigExt.getProductAttr()) {
                    giftType = Boolean.TRUE;
                } else {
                    BigDecimal rewardMoney = ComputeUtil.computeMultiply(acGiftConfigExt.getRetailPrice(), acGiftConfigExt.getQuantity());
                    // 增加trade
                    // 虚拟产品 进账户 更新账户
                    memberServiceApi.updateCuMemberAccountByDraw(acDrawRewardNum.getPkMember(), acDrawRewardNum.getPkCountry(),
                            acDrawGiftRuleConfig.getGrantWay(), rewardMoney);
                }
            }
            if (giftType) {
                // 提货配置
                AcPickUpConfig acPickUpConfig = acPickUpConfigService.queryAcPickUpConfig(AcPickUpConfig.builder().pkBaseId(acDrawRewardNum.getPkBaseId()).build());
                if (acPickUpConfig == null) {
                    throw new RuntimeException();
                }
                // 计算截至日期，根据当前日期 + 截至日期
                Date receiveTime = DateUtils.afterDateTime(acPickUpConfig.getDeliveryDays(), ChronoUnit.DAYS);
                // 加入提货单
                acPickService.addPickByActivity(acDrawRewardNum.getPkMember(), EActType.LOTTERY_DRAW.getValue(), acPickUpConfig.getPkId(), rewardRecordId,
                        1, acDrawGiftRuleConfig.getGiftCover(), receiveTime, acPickUpConfig.getPkPostage(),
                        acDrawRewardNum.getPkMember(), acDrawRewardNum.getPkCountry(), acDrawRewardNum.getPkBaseId());
            }
            return acDrawGiftRuleConfig;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("抽奖失败，错误信息{}", e.getMessage());
            if (rewardRecordId != null && rewardRecordId > 0) {
                redisService.rightPushCacheList(key, rewardRecordId);
            }
            throw new RuntimeException(e);
        }
    }


    @Override
    public List<AcDrawRewardRecordExt> queryCuMemberGiftRecordByCondition(AcDrawRecordParam acDrawRecordParam) {
        if (acDrawRecordParam.getEndDate() != null) {
            acDrawRecordParam.setEndDate(DateUtils.afterDate(1, ChronoUnit.DAYS, acDrawRecordParam.getEndDate()));
        }
        return baseMapper.queryCuMemberGiftRecordByCondition(acDrawRecordParam);
    }

    private void generateAwardRecords(List<AcDrawGiftRuleConfigExt> drawGiftConfigList, Long pkBaseId) {
        // 每次生成10轮的值放入redis中，每次redis自动获取
        // 1 获取所有奖品
        if (drawGiftConfigList != null && drawGiftConfigList.size() > 0) {
            // 获取数量
            Iterator<AcDrawGiftRuleConfigExt> iterator = drawGiftConfigList.iterator();
            Map<Long, AcDrawGiftRuleConfigExt> chanceConfigMap = new HashMap<>();
            // 主键  抽奖的奖品
            List<Long> rewardRecordList = new ArrayList<>();
            List<Long> addRewardRecordList = new ArrayList<>();
            // 设置抽奖次数
            Integer quantity = 0;
            while (iterator.hasNext()) {
                AcDrawGiftRuleConfigExt acDrawGiftRuleConfig = iterator.next();
                quantity += acDrawGiftRuleConfig.getGiftNumber();
                // 等待到达次数后在加入奖池
                if (acDrawGiftRuleConfig.getMinNumber() > 0) {
                    chanceConfigMap.put(acDrawGiftRuleConfig.getPkId(), acDrawGiftRuleConfig);
                } else {
                    addPrize(rewardRecordList, acDrawGiftRuleConfig);
                }
            }
            // 设置已抽奖次数
            Integer userQuantity = 1;
            while (quantity > 0) {
                // 判断抽奖次数是否到达最低，到达最低加入新的奖品
                Iterator<Long> chanceIterator = chanceConfigMap.keySet().iterator();
                while (chanceIterator.hasNext()) {
                    AcDrawGiftRuleConfigExt acDrawGiftRuleConfig = chanceConfigMap.get(chanceIterator.next());
                    if (acDrawGiftRuleConfig.getMinNumber() <= userQuantity) {
                        addPrize(rewardRecordList, acDrawGiftRuleConfig);
                        chanceIterator.remove();
                    }
                }
                // 抽奖逻辑
                double prizeSize = Math.floor(Math.random() * rewardRecordList.size());
                log.info("开始抽奖，第{}次抽奖,抽到的奖品是{}", userQuantity, rewardRecordList.get((int) prizeSize));
                addRewardRecordList.add(rewardRecordList.get((int) prizeSize));
                userQuantity++;
                quantity--;
            }
            redisService.setCacheList(CacheConstants.DRAW_REWARD_GIFT + pkBaseId, addRewardRecordList);
        }
    }

    private void addPrize(List<Long> awardRecordList, AcDrawGiftRuleConfigExt acDrawGiftRuleConfig) {
        for (int i = 0; i < acDrawGiftRuleConfig.getGiftNumber(); i++) {
            awardRecordList.add(acDrawGiftRuleConfig.getPkId());
        }
    }

}
