package com.ruoyi.cai.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.Rank;
import com.ruoyi.cai.domain.User;
import com.ruoyi.cai.dto.admin.vo.RankAdminVo;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.enums.account.AccountChangeCodeEnum;
import com.ruoyi.cai.manager.IdManager;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.mapper.AccountMapper;
import com.ruoyi.cai.mapper.RankMapper;
import com.ruoyi.cai.rank.RankManager;
import com.ruoyi.cai.rank.RankNode;
import com.ruoyi.cai.service.AccountChangeLogService;
import com.ruoyi.cai.service.RankService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.cai.util.CaiDateUtil;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 榜单Service业务层处理
 *
 * @author 77
 * @date 2024-01-20
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class RankServiceImpl extends ServiceImpl<RankMapper,Rank> implements RankService {

    @Autowired
    private RankManager rankManager;
    @Autowired
    private UserService userService;
    @Resource
    private AccountMapper accountMapper;
    @Autowired
    private SystemConfigManager systemConfigManager;
    @Autowired
    private AccountChangeLogService accountChangeLogService;

    @Override
    public void saveDayRank(LocalDate date, Integer type) {
        List<RankNode> rank;
        if(type == 1){
            rank = rankManager.getLoveRankDay(date, 30);
        }else if(type == 2){
            rank = rankManager.getInviteRankDay(date, 30);
        }else{
            throw new ServiceException("type 参数错误");
        }
        if(CollectionUtil.isEmpty(rank)){
            return;
        }
        boolean exists = this.exists(Wrappers.lambdaQuery(Rank.class)
                .eq(Rank::getType, type)
                .eq(Rank::getRankBeginTime, date));
        if(exists){
            log.error("本期榜单已经持久化 date={},type={}",date,type);
            return;
        }
        ReentrantLock lock = new ReentrantLock();
        try {
            lock.lock();
            exists = this.exists(Wrappers.lambdaQuery(Rank.class)
                    .eq(Rank::getType, type)
                    .eq(Rank::getRankBeginTime, date));
            if(exists){
                log.error("本期榜单已经持久化 date={},type={}",date,type);
                return;
            }
            List<Rank> res = new ArrayList<>();
            for (int i = 0; i < rank.size(); i++) {
                RankNode rankNode = rank.get(i);
                Rank db = new Rank();
                db.setType(type);
                db.setDataType(1);
                db.setRankTime(date.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
                db.setRankBeginTime(date);
                db.setRankEndTime(date);
                db.setOrderRank(i+1);
                db.setNum(rankNode.getScore());
                db.setUserId(rankNode.getUserId());
                res.add(db);
            }
            this.saveBatch(res);
        }finally {
            lock.unlock();
        }

    }

    @Override
    public void saveWeekRank(LocalDate date, Integer type) {
        List<RankNode> rank;
        if(type == 1){
            rank = rankManager.getLoveRankWeek(date, 30);
        }else if(type == 2){
            rank = rankManager.getInviteRankWeek(date, 30);
        }else{
            throw new ServiceException("type 参数错误");
        }
        if(CollectionUtil.isEmpty(rank)){
            return;
        }
        boolean exists = this.exists(Wrappers.lambdaQuery(Rank.class)
                .eq(Rank::getType, type)
                .eq(Rank::getRankBeginTime, date));
        if(exists){
            log.error("本期榜单已经持久化 date={},type={}",date,type);
            return;
        }
        ReentrantLock lock = new ReentrantLock();
        try {
            lock.lock();
            exists = this.exists(Wrappers.lambdaQuery(Rank.class)
                    .eq(Rank::getType, type)
                    .eq(Rank::getRankBeginTime, date));
            if(exists){
                log.error("本期榜单已经持久化 date={},type={}",date,type);
                return;
            }
            List<Rank> res = new ArrayList<>();
            LocalDate endDate = date.plusDays(7);
            String rankTime = date.format(DateTimeFormatter.ofPattern("yyyyMMdd")) +"-"+endDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            for (int i = 0; i < rank.size(); i++) {
                RankNode rankNode = rank.get(i);
                Rank db = new Rank();
                db.setType(type);
                db.setDataType(2);
                db.setRankTime(rankTime);
                db.setRankBeginTime(date);
                db.setRankEndTime(endDate);
                db.setOrderRank(i+1);
                db.setNum(rankNode.getScore());
                db.setUserId(rankNode.getUserId());
                res.add(db);
            }
            this.saveBatch(res);
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void saveMonthRank(LocalDate date, Integer type) {
        List<RankNode> rank;
        if(type == 1){
            rank = rankManager.getLoveRankDay(date, 30);
        }else if(type == 2){
            rank = rankManager.getInviteRankDay(date, 30);
        }else{
            throw new ServiceException("type 参数错误");
        }
        if(CollectionUtil.isEmpty(rank)){
            return;
        }
        boolean exists = this.exists(Wrappers.lambdaQuery(Rank.class)
                .eq(Rank::getType, type)
                .eq(Rank::getRankBeginTime, date));
        if(exists){
            log.error("本期榜单已经持久化 date={},type={}",date,type);
            return;
        }
        ReentrantLock lock = new ReentrantLock();
        try {
            lock.lock();
            exists = this.exists(Wrappers.lambdaQuery(Rank.class)
                    .eq(Rank::getType, type)
                    .eq(Rank::getRankBeginTime, date));
            if(exists){
                log.error("本期榜单已经持久化 date={},type={}",date,type);
                return;
            }
            LocalDate endDate = date.plusMonths(1).plusDays(-1);
            List<Rank> res = new ArrayList<>();
            for (int i = 0; i < rank.size(); i++) {
                RankNode rankNode = rank.get(i);
                Rank db = new Rank();
                db.setType(type);
                db.setDataType(3);
                db.setRankTime(date.format(DateTimeFormatter.ofPattern("yyMM")));
                db.setRankBeginTime(date);
                db.setRankEndTime(endDate);
                db.setOrderRank(i+1);
                db.setNum(rankNode.getScore());
                db.setUserId(rankNode.getUserId());
                res.add(db);
            }
            this.saveBatch(res);
        }finally {
            lock.unlock();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void giveRank(Long rankId) {
        Rank rank = this.getById(rankId);
        if(rank == null){
            throw new ServiceException("领取失败,请联系客服");
        }
        User user = userService.getById(rank.getUserId());
        if(user == null){
            throw new ServiceException("用户不存在");
        }
        List<String> priceList = null;
        if(rank.getType().equals(1)){ // 魅力
            if(rank.getDataType().equals(1)){
                priceList = systemConfigManager.getSystemConfigOfList(SystemConfigEnum.RANK_LOVE_DAY_AWARD);
            }else if(rank.getDataType().equals(2)){
                priceList = systemConfigManager.getSystemConfigOfList(SystemConfigEnum.RANK_LOVE_WEEK_AWARD);
            }
        }else if(rank.getType().equals(2)){ // 邀请
            if(rank.getDataType().equals(1)){
                priceList = systemConfigManager.getSystemConfigOfList(SystemConfigEnum.RANK_INVITE_DAY_AWARD);
            }else if(rank.getDataType().equals(2)){
                priceList = systemConfigManager.getSystemConfigOfList(SystemConfigEnum.RANK_INVITE_WEEK_AWARD);
            }
        }
        if(priceList == null){
            throw new ServiceException("领取失败,请联系客服");
        }
        Long traceId = IdManager.nextId();
        boolean update = this.update(Wrappers.lambdaUpdate(Rank.class)
                .eq(Rank::getId, rankId)
                .eq(Rank::getDraw, false)
                .set(Rank::getTraceId,traceId)
                .set(Rank::getDraw, true));
        if(!update){
            throw new ServiceException("您已经领取奖励哦");
        }
        String priceStr = priceList.get(rank.getOrderRank() - 1);
        Long price = Long.valueOf(priceStr);
        accountMapper.incsIncomeCoin(rank.getUserId(),price);
        accountChangeLogService.saveLogNoAdmin(user.getId(),user.getUsercode(), AccountChangeCodeEnum.RANK_AWARD,price,traceId);
    }

    @Override
    public List<RankNode> getLoveRankLastWeek(int limit) {
        LocalDate beginDate = CaiDateUtil.getLastWeekOne(LocalDate.now());
        List<Rank> list = this.list(Wrappers.lambdaQuery(Rank.class)
                .eq(Rank::getRankBeginTime, beginDate)
                .eq(Rank::getType, 1)
                .eq(Rank::getDataType, 2)
                .le(Rank::getOrderRank,limit)
                .orderByAsc(Rank::getOrderRank)
                .last("limit " + limit));
        return list.stream().map(i -> {
            RankNode rankNode = new RankNode();
            rankNode.setScore(i.getNum());
            rankNode.setUserId(i.getUserId());
            rankNode.setRankId(i.getId());
            rankNode.setDraw(i.getDraw());
            return rankNode;
        }).collect(Collectors.toList());
    }

    @Override
    public List<RankNode> getLoveRankLastDay(int limit) {
        LocalDate beginDate = LocalDate.now().plusDays(-1);
        List<Rank> list = this.list(Wrappers.lambdaQuery(Rank.class)
                .eq(Rank::getRankBeginTime, beginDate)
                .eq(Rank::getType, 1)
                .eq(Rank::getDataType, 1)
                .le(Rank::getOrderRank,limit)
                .orderByAsc(Rank::getOrderRank)
                .last("limit " + limit));
        return list.stream().map(i -> {
            RankNode rankNode = new RankNode();
            rankNode.setScore(i.getNum());
            rankNode.setUserId(i.getUserId());
            rankNode.setRankId(i.getId());
            rankNode.setDraw(i.getDraw());
            return rankNode;
        }).collect(Collectors.toList());
    }

    @Override
    public List<RankNode> getInviteRankLastWeek(int limit) {
        LocalDate beginDate = CaiDateUtil.getLastWeekOne(LocalDate.now());
        List<Rank> list = this.list(Wrappers.lambdaQuery(Rank.class)
                .eq(Rank::getRankBeginTime, beginDate)
                .eq(Rank::getType, 2)
                .eq(Rank::getDataType, 2)
                .le(Rank::getOrderRank,limit)
                .orderByDesc(Rank::getOrderRank)
                .last("limit " + limit));
        return list.stream().map(i -> {
            RankNode rankNode = new RankNode();
            rankNode.setScore(i.getNum());
            rankNode.setUserId(i.getUserId());
            rankNode.setRankId(i.getId());
            rankNode.setDraw(i.getDraw());
            return rankNode;
        }).collect(Collectors.toList());
    }

    @Override
    public List<RankNode> getInviteRankLastDay(int limit) {
        LocalDate beginDate = LocalDate.now().plusDays(-1);
        List<Rank> list = this.list(Wrappers.lambdaQuery(Rank.class)
                .eq(Rank::getRankBeginTime, beginDate)
                .eq(Rank::getType, 2)
                .eq(Rank::getDataType, 1)
                .le(Rank::getOrderRank,limit)
                .orderByDesc(Rank::getOrderRank)
                .last("limit " + limit));
        return list.stream().map(i -> {
            RankNode rankNode = new RankNode();
            rankNode.setScore(i.getNum());
            rankNode.setUserId(i.getUserId());
            rankNode.setRankId(i.getId());
            rankNode.setDraw(i.getDraw());
            return rankNode;
        }).collect(Collectors.toList());
    }

    @Override
    public Page<RankAdminVo> pageAdmin(PageQuery pageQuery, RankAdminVo bo) {
        return baseMapper.pageAdmin(pageQuery.build(),bo);
    }
}
