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.CourseScheduleDao;
import com.yonge.cooleshow.biz.dal.dao.StudentDao;
import com.yonge.cooleshow.biz.dal.dao.StudentStarDao;
import com.yonge.cooleshow.biz.dal.entity.Student;
import com.yonge.cooleshow.biz.dal.entity.StudentStar;
import com.yonge.cooleshow.biz.dal.service.MusicSheetService;
import com.yonge.cooleshow.common.enums.CacheNameEnum;
import org.redisson.api.RBucket;
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.StudentTotal;
import com.yonge.cooleshow.biz.dal.vo.StudentTotalVo;
import com.yonge.cooleshow.biz.dal.dto.search.StudentTotalSearch;
import com.yonge.cooleshow.biz.dal.dao.StudentTotalDao;
import com.yonge.cooleshow.biz.dal.service.StudentTotalService;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;


@Service
public class StudentTotalServiceImpl extends ServiceImpl<StudentTotalDao, StudentTotal> implements StudentTotalService {
    private final static Logger log = LoggerFactory.getLogger(StudentTotalServiceImpl.class);
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private StudentStarDao studentStarDao;
    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private MusicSheetService musicSheetService;

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

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

    @Override
    public List<StudentTotal> queryStudentTotal() {
        List<Student> students = studentDao.selectList(Wrappers.<Student>emptyWrapper()
                .select("userId"));
        //查询关注老师数
        List<StudentTotalVo> studentStarTotal = studentStarDao.queryStudentTotal();
        Map<Long, StudentTotalVo> studentStarMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(studentStarTotal)) {
            studentStarMap = studentStarTotal.stream().collect(Collectors.toMap(StudentTotalVo::getUserId, o -> o));
        }

        //查询已上未上课时数
        List<StudentTotalVo> studentCourseTotal = courseScheduleDao.queryStudentTotal(null);
        Map<Long, StudentTotalVo> studentCourseMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(studentCourseTotal)) {
            studentCourseMap = studentCourseTotal.stream().collect(Collectors.toMap(StudentTotalVo::getUserId, o -> o));
        }

        //乐谱统计
        List<StudentTotalVo> studentMusicTotal = musicSheetService.queryStudentTotal(null);
        Map<Long, StudentTotalVo> studentMusicMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(studentMusicTotal)) {
            studentMusicMap = studentMusicTotal.stream().collect(Collectors.toMap(StudentTotalVo::getUserId, o -> o));
        }

        List<StudentTotal> resultList = new ArrayList<>();
        for (Student student : students) {
            StudentTotal studentTotal = new StudentTotal();
            studentTotal.setUserId(student.getUserId());
            StudentTotalVo starTotal = studentStarMap.get(student.getUserId());
            if (null != starTotal) {
                studentTotal.setStarTeacherNum(null == starTotal.getStarTeacherNum() ? 0 : starTotal.getStarTeacherNum());
            }
            StudentTotalVo courseTotal = studentCourseMap.get(student.getUserId());
            if (null != courseTotal) {
                studentTotal.setFinshHours(null == courseTotal.getFinshHours() ? 0 : courseTotal.getFinshHours());
                studentTotal.setUnfinshHours(null == courseTotal.getUnfinshHours() ? 0 : courseTotal.getUnfinshHours());
            }

            StudentTotalVo musicTotal = studentMusicMap.get(student.getUserId());
            if (null != musicTotal) {
                studentTotal.setMusicAlbumNum(null == musicTotal.getMusicAlbumNum() ? 0 : musicTotal.getMusicAlbumNum());
                studentTotal.setMusicSheetNum(null == musicTotal.getMusicSheetNum() ? 0 : musicTotal.getMusicSheetNum());
            }

            resultList.add(studentTotal);
            redissonClient.getBucket(CacheNameEnum.STUDENT_TOTAL.getRedisKey(studentTotal.getUserId()))
                    .set(studentTotal);
        }
        saveOrUpdateBatch(resultList);
        return resultList;
    }

    @Override
    public StudentTotal getTotalById(Long id) {
        RBucket<Object> bucket = redissonClient.getBucket(CacheNameEnum.STUDENT_TOTAL.getRedisKey(id));
        if (bucket.isExists()) {
            return (StudentTotal) bucket.get();
        } else {
            //去数据库查询
            StudentTotal total = totalStudentTotalById(id);
            if (null != total) {
                bucket.set(total);
            }
            return total;
        }
    }

    @Override
    public void updateTotalCache(StudentTotal studentTotal) {
        if (null != studentTotal && null != studentTotal.getUserId()) {
            redissonClient.getBucket(CacheNameEnum.STUDENT_TOTAL.getRedisKey(studentTotal.getUserId())).set(studentTotal);
        }
    }


    private StudentTotal totalStudentTotalById(Long id) {
        StudentTotal studentTotal = new StudentTotal();
        studentTotal.setUserId(id);

        //查询关注老师数
        Integer starTeacherNum = studentStarDao.selectCount(Wrappers.<StudentStar>lambdaQuery()
                .eq(StudentStar::getStudentId, id)
        );
        studentTotal.setStarTeacherNum(starTeacherNum);

        List<StudentTotalVo> studentScheduleTotal = courseScheduleDao.queryStudentTotal(id);
        if (!CollectionUtils.isEmpty(studentScheduleTotal)) {
            StudentTotalVo scheduleTotal = studentScheduleTotal.get(0);
            if (null != scheduleTotal) {
                studentTotal.setFinshHours(null == scheduleTotal.getFinshHours() ? 0 : scheduleTotal.getFinshHours());
                studentTotal.setUnfinshHours(null == scheduleTotal.getUnfinshHours() ? 0 : scheduleTotal.getUnfinshHours());
            }
        }

        List<StudentTotalVo> studentMuiscTotal = musicSheetService.queryStudentTotal(id);
        if (!CollectionUtils.isEmpty(studentMuiscTotal)) {
            StudentTotalVo muiscTotal = studentMuiscTotal.get(0);
            if (null != muiscTotal) {
                studentTotal.setMusicAlbumNum(null == muiscTotal.getMusicAlbumNum() ? 0 : muiscTotal.getMusicAlbumNum());
                studentTotal.setMusicSheetNum(null == muiscTotal.getMusicSheetNum() ? 0 : muiscTotal.getMusicSheetNum());
            }
        }

        redissonClient.getBucket(CacheNameEnum.STUDENT_TOTAL.getRedisKey(studentTotal.getUserId()))
                .set(studentTotal);

        StudentTotal old = getById(id);
        if (null != old) {
            updateById(studentTotal);
        } else {
            save(studentTotal);
        }
        return studentTotal;
    }

    int batchSize = 100;

    /***
     * 批量更新插入
     * @author liweifan
     * @param: resultList
     * @updateTime 2022/4/12 16:52
     */
    private void saveOrUpdateBatch(List<StudentTotal> resultList) {
        List<StudentTotal> studentTotals = baseMapper.selectList(Wrappers.<StudentTotal>emptyWrapper().select("userId"));
        Map<Long, StudentTotal> studentTotalMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(studentTotals)) {
            studentTotals.forEach(o -> studentTotalMap.put(o.getUserId(), o));
        }
        //批量插入
        List<StudentTotal> saveList = resultList.stream()
                .filter(o -> null == studentTotalMap.get(o.getUserId())).collect(Collectors.toList());
        batchOpera(saveList, this::saveBatch);

        //批量更新
        List<StudentTotal> updateList = resultList.stream()
                .filter(o -> null != studentTotalMap.get(o.getUserId())).collect(Collectors.toList());
        batchOpera(updateList, this::updateBatch);
    }

    private void batchOpera(List<StudentTotal> resultList, Consumer<List<StudentTotal>> action) {
        int start = 0;
        while (true) {
            int end = start + batchSize;
            List<StudentTotal> subList;
            if (resultList.size() > end) {
                subList = resultList.subList(start, start + batchSize);
            } else {
                subList = resultList.subList(start, resultList.size());
            }
            //处理sql
            action.accept(subList);
            if (end >= resultList.size()) {
                break;
            }
            start += batchSize;
        }
    }

    private void updateBatch(List<StudentTotal> resultList) {
        for (StudentTotal total : resultList) {
            updateById(total);
        }
    }
}