package com.td.WaDa.game.comcnttosnb.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.td.WaDa.common.pojo.Result;
import com.td.WaDa.common.service.CommonServiceImpl;
import com.td.WaDa.config.enums.ResultEnum;
import com.td.WaDa.game.comcnttosnb.pojo.ComCntToSnb;
import com.td.WaDa.game.comcnttosnb.repository.ComCntToSnbRespository;
import com.td.WaDa.game.comcnttosnb.vo.ComCntToSnbVo;
import com.td.WaDa.game.comsnbtran.service.ComSnbTranService;
import com.td.WaDa.game.comsnbtran.vo.ComSnbTranVo;
import com.td.WaDa.game.comusers.service.ComUsersService;
import com.td.WaDa.game.comusers.vo.ComUsersVo;
import com.td.WaDa.util.CopyUtil;
import com.td.WaDa.util.RedisData;
import com.td.WaDa.util.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class ComCntToSnbServiceImpl extends CommonServiceImpl<ComCntToSnbVo, ComCntToSnb, Integer> implements ComCntToSnbService {

    @Autowired
    private ComCntToSnbRespository comCntToSnbRespository;
    @Autowired
    private ComUsersService comUsersService;
    @Autowired
    private ComSnbTranService comSnbTranService;
    @Autowired
    private RedisLock redisLock;

    /**
     * 通过userid查看snb领取情况
     *
     * @param userId 用户id
     * @return {@link ComCntToSnbVo}
     */
    @Override
    public ComCntToSnbVo findByUserId(String userId) {
        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
        if (comUsersVo == null || StrUtil.isBlank(comUsersVo.getAddress())) {
            return null;
        }
        ComCntToSnb comCntToSnb = comCntToSnbRespository.findByAddress(comUsersVo.getAddress());
        return comCntToSnb == null ? null : CopyUtil.copy(comCntToSnb, ComCntToSnbVo.class);
    }

    /**
     * 通过userid领取snb
     *
     * @param userId 用户id
     * @return {@link Result}<{@link String}>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<String> receiveByUserId(String userId) {
        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
        if (comUsersVo == null || StrUtil.isBlank(comUsersVo.getAddress())) {
            return Result.of(null, false, "未找到用户或钱包地址");
        }
        ComCntToSnb comCntToSnb = comCntToSnbRespository.findByAddress(comUsersVo.getAddress());
        if (comCntToSnb == null || comCntToSnb.getStatus().equals(0)) {
            return Result.of(null, false, "非法领取");
        }
        if (comCntToSnb.getReceivedDay() >= comCntToSnb.getTotalDay()) {
            return Result.of(null, false, "已全部领取完毕");
        }
        if (comCntToSnb.getBeginTime() == null) {
            return Result.of(null, false, "未设置领取开始时间");
        }
        // 服务器时间
        Date serverDate = new Date();
        // 未到领取时间
        if (DateUtil.compare(comCntToSnb.getBeginTime(), serverDate) > 0) {
            return Result.of(null, false, "未到领取时间");
        }
        // 计算本次可领天数. 服务器时间 - 开始时间 - 已领取天数
        Integer day = Convert.toInt(DateUtil.betweenDay(comCntToSnb.getBeginTime(), serverDate, true)) - comCntToSnb.getReceivedDay() + 1;
        log.info("领取人：" + userId);
        log.info("领取开始时间：" + DateUtil.format(comCntToSnb.getBeginTime(), "yyyy-MM-dd"));
        log.info("服务器时间：" + DateUtil.format(serverDate, "yyyy-MM-dd"));
        log.info("已领取：" + comCntToSnb.getReceivedDay() + "天，本次领取天数：" + day + "天");
        if (day <= 0) {
            return Result.of(null, false, "领取失败，今日已领取");
        }
        long time = System.currentTimeMillis() + RedisData.getSnbTimeout();
        String redisKey = StrUtil.format("SNB_SAVE_{}", userId);

        try {
            // redis锁
            if (!redisLock.lock(redisKey, String.valueOf(time))) {
                return Result.of(null, false, ResultEnum.SEED_SALE_SAVE_LOCK.getMessage(), ResultEnum.SEED_SALE_SAVE_LOCK.getCode());
            }
            // 单次领取数量
            BigDecimal receiveNum = NumberUtil.round(NumberUtil.div(comCntToSnb.getTotalQuantity(), comCntToSnb.getTotalDay()), 2, RoundingMode.DOWN);
            log.info("每天领取数量：" + receiveNum);
            // 本次领取量
            BigDecimal thisReceiveQuantity;
            // 特例：领取周期100天，这个人间隔100以后上线
            if (day >= comCntToSnb.getTotalDay() - comCntToSnb.getReceivedDay()) {
                day = comCntToSnb.getTotalDay() - comCntToSnb.getReceivedDay();
                log.info("由于本次领取天数大于剩余领取天数，所以最终领取天数：" + day);
                thisReceiveQuantity = NumberUtil.sub(comCntToSnb.getTotalQuantity(), NumberUtil.mul(comCntToSnb.getReceivedDay(), receiveNum));
            } else {
                // 本次领取量
                thisReceiveQuantity = NumberUtil.mul(receiveNum, day);
            }

            comCntToSnb.setReceivedQuantity(comCntToSnb.getReceivedQuantity().add(thisReceiveQuantity));
            // 本次领取天数
            comCntToSnb.setReceivedDay(comCntToSnb.getReceivedDay() + day);
            // 更新领取信息
            comCntToSnbRespository.save(comCntToSnb);

            // 更新comUsers snb+
            comUsersVo.setSnbPart(comUsersVo.getSnbPart() + Convert.toDouble(thisReceiveQuantity));
            comUsersService.save(comUsersVo);

            // 插入comSnbTran
            ComSnbTranVo snbTran = new ComSnbTranVo();
            //记录兑换id
            snbTran.setTranId(Convert.toStr(comCntToSnb.getId()));
            snbTran.setUserId(userId);
            snbTran.setTranName("领取SNB");
            snbTran.setTranType(8);
            snbTran.setTranAmount(1);
            snbTran.setTranPrice(0);
            snbTran.setTranDescribe(StrUtil.format("user领取SNB、本次领取：{},剩余：{}", thisReceiveQuantity, comCntToSnb.getTotalQuantity().subtract(comCntToSnb.getReceivedQuantity())));
            snbTran.setIsAdd(1);
            snbTran.setBeforeSnb(comUsersVo.getSnb());
            snbTran.setTranSnb(0);
            snbTran.setAfterSnb(comUsersVo.getSnb());
            // 交易的部分数据
            snbTran.setTranAmountPart(Convert.toDouble(thisReceiveQuantity));
            // 交易的snb
            snbTran.setTranSnbPart(Convert.toDouble(thisReceiveQuantity));
            // 交易之前的snb
            snbTran.setBeforeSnbPart(Convert.toDouble(comCntToSnb.getReceivedQuantity().subtract(thisReceiveQuantity)));
            // 交易完成后的snb
            snbTran.setAfterSnbPart(Convert.toDouble(comCntToSnb.getReceivedQuantity()));
            snbTran.setCreateTime(serverDate);
            comSnbTranService.save(snbTran);

            return Result.of(null, false, "领取成功");

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            // redis解锁
            redisLock.unlock(redisKey, String.valueOf(time));
        }
    }



    @Override
    public List<ComCntToSnb> getBetweenTimeList(Date begin, Date end) {
        Specification specification = new Specification<ComCntToSnb>() {
            @Override
            public Predicate toPredicate(Root<ComCntToSnb> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                predicates.add(criteriaBuilder.between(root.get("beginTime"), begin, end));
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        return comCntToSnbRespository.findAll(specification);
    }
}
