package com.qmth.exam.reserve.cache.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qmth.exam.reserve.bean.apply.ApplyRecordCacheBean;
import com.qmth.exam.reserve.bean.applytask.CurrentApplyTaskVO;
import com.qmth.exam.reserve.cache.CacheConstants;
import com.qmth.exam.reserve.cache.RedisClient;
import com.qmth.exam.reserve.entity.StudentApplyEntity;
import com.qmth.exam.reserve.service.ApplyTaskService;
import com.qmth.exam.reserve.service.ExamSiteService;
import com.qmth.exam.reserve.service.StudentApplyService;
import com.qmth.exam.reserve.service.StudentService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class ApplyTaskCacheService implements CacheConstants {

    private static final Logger log = LoggerFactory.getLogger(ApplyTaskCacheService.class);

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private ApplyTaskService applyTaskService;

    @Autowired
    private ExamSiteService examSiteService;

    @Autowired
    private StudentService studentService;

    @Autowired
    private StudentApplyService studentApplyService;

    /**
     * 获取当前启用的预约任务缓存
     */
    public CurrentApplyTaskVO currentApplyTask(Long orgId) {
        String cacheKey = String.format(CACHE_CURRENT_APPLY_TASK, orgId);
        CurrentApplyTaskVO value = redisClient.get(cacheKey, CurrentApplyTaskVO.class);
        if (value != null) {
            return value;
        }

        value = applyTaskService.currentApplyTask(orgId);
        if (value == null) {
            return null;
        }

        redisClient.set(cacheKey, value, 6, TimeUnit.HOURS);
        log.info("SET cacheKey:{} curApplyTaskId:{}", cacheKey, value.getTaskId());

        return value;
    }

    public void clearCurrentApplyTaskCache(Long orgId) {
        String cacheKey = String.format(CACHE_CURRENT_APPLY_TASK, orgId);
        redisClient.delete(cacheKey);
        log.warn("DELETE cacheKey:{}", cacheKey);
    }

    /**
     * 获取某考生的“允许预约时段次数”
     */
    public int getStudentApplyNumber(Long studentId) {
        String cacheKey = String.format(CACHE_STUDENT_APPLY_NUMBER, studentId);
        Integer value = redisClient.get(cacheKey, Integer.class);
        if (value != null) {
            return value;
        }

        value = studentService.findStudentApplyNumberById(studentId);
        redisClient.set(cacheKey, value, 10, TimeUnit.MINUTES);
        log.info("SET cacheKey:{} value:{}", cacheKey, value);

        return value;
    }

    public void clearStudentApplyNumberCache(Long studentId) {
        String cacheKey = String.format(CACHE_STUDENT_APPLY_NUMBER, studentId);
        redisClient.delete(cacheKey);
        log.warn("DELETE cacheKey:{}", cacheKey);
    }

    /**
     * 获取某考点的“可预约总量”
     */
    public int getApplyTotalCount(Long examSiteId) {
        String cacheKey = String.format(CACHE_APPLY_TOTAL, examSiteId);
        Integer value = redisClient.get(cacheKey, Integer.class);
        if (value != null) {
            return value;
        }

        value = examSiteService.countExamSiteCapacityById(examSiteId);
        redisClient.set(cacheKey, value, 10, TimeUnit.MINUTES);
        log.info("SET cacheKey:{} value:{}", cacheKey, value);

        return value;
    }

    public void clearApplyTotalCountCache(Long examSiteId) {
        String cacheKey = String.format(CACHE_APPLY_TOTAL, examSiteId);
        redisClient.delete(cacheKey);
        log.warn("DELETE cacheKey:{}", cacheKey);
    }

    /**
     * 获取某考点某时段的“已预约数量”
     */
    public int getApplyFinishCount(Long examSiteId, Long timePeriodId) {
        String cacheKey = String.format(CACHE_APPLY_FINISH, examSiteId, timePeriodId);
        if (redisClient.exist(cacheKey)) {
            return (int) redisClient.getRedissonClient().getAtomicLong(cacheKey).get();
        }

        RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey);

        int value = studentApplyService.countApplyFinishForExamSiteAndTimePeriod(examSiteId, timePeriodId);
        atomic.set(value);
        atomic.expire(30, TimeUnit.DAYS);
        log.info("SET cacheKey:{} value:{}", cacheKey, value);

        return value;
    }

    /**
     * 获取当前预约时段剩余可约数量
     */
    public int getApplyAvailableCount(Long examSiteId, Long timePeriodId) {
        int totalCount = this.getApplyTotalCount(examSiteId);
        int finishCount = this.getApplyFinishCount(examSiteId, timePeriodId);
        int availableCount = totalCount - finishCount;
        return Math.max(availableCount, 0);
    }

    /**
     * 累加 某考点某时段的“已预约数量”
     */
    public void increaseApplyFinishCount(Long examSiteId, Long timePeriodId) {
        String cacheKey = String.format(CACHE_APPLY_FINISH, examSiteId, timePeriodId);
        RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey);
        atomic.incrementAndGet();
    }

    /**
     * 累减 某考点某时段的“已预约数量”
     */
    public void decreaseApplyFinishCount(Long examSiteId, Long timePeriodId) {
        String cacheKey = String.format(CACHE_APPLY_FINISH, examSiteId, timePeriodId);
        RAtomicLong atomic = redisClient.getRedissonClient().getAtomicLong(cacheKey);
        if (atomic.get() > 0) {
            atomic.decrementAndGet();
        }
    }

    /**
     * 获取某考生的 已完成预约次数
     */
    public int getStudentApplyFinishCount(Long studentId) {
        // Map<String, ApplyRecordCacheBean> maps = this.getStudentApplyRecords(studentId);
        // int studentApplyFinishCount = 0;
        // for (ApplyRecordCacheBean bean : maps.values()) {
        //     if (!bean.getCancel()) {
        //         studentApplyFinishCount++;
        //     }
        // }
        // return studentApplyFinishCount;

        LambdaQueryWrapper<StudentApplyEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StudentApplyEntity::getStudentId, studentId);
        wrapper.eq(StudentApplyEntity::getCancel, Boolean.FALSE);
        return studentApplyService.count(wrapper);
    }

    /**
     * 获取某考生的 所有的“预约记录”缓存
     */
    public Map<String, ApplyRecordCacheBean> getStudentApplyRecords(Long studentId) {
        String cacheKey = String.format(CACHE_STUDENT_APPLY_RECORD, studentId);
        if (!redisClient.exist(cacheKey)) {
            Map<String, ApplyRecordCacheBean> maps = this.getStudentApplyRecordsFromDB(studentId);
            if (MapUtils.isEmpty(maps)) {
                return new HashMap<>();
            }

            for (ApplyRecordCacheBean bean : maps.values()) {
                this.saveStudentApplyRecord(bean);
            }

            redisClient.expire(cacheKey, 30, TimeUnit.DAYS);
            return maps;
        }

        return redisClient.getEntriesForHash(cacheKey, ApplyRecordCacheBean.class);
    }

    /**
     * 获取某考生的 所有的“预约记录”
     */
    public Map<String, ApplyRecordCacheBean> getStudentApplyRecordsFromDB(Long studentId) {
        LambdaQueryWrapper<StudentApplyEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StudentApplyEntity::getStudentId, studentId);
        List<StudentApplyEntity> list = studentApplyService.list(wrapper);

        if (CollectionUtils.isEmpty(list)) {
            return new HashMap<>();
        }

        Map<String, ApplyRecordCacheBean> maps = new HashMap<>();
        for (StudentApplyEntity entity : list) {
            ApplyRecordCacheBean bean = new ApplyRecordCacheBean();
            bean.setStudentId(entity.getStudentId());
            bean.setExamSiteId(entity.getExamSiteId());
            bean.setTimePeriodId(entity.getTimePeriodId());
            bean.setCancel(entity.getCancel());
            bean.setOperateId(entity.getOperateId());
            bean.setOperateTime(entity.getUpdateTime());

            String hashKey = String.format("%s_%s", entity.getExamSiteId(), entity.getTimePeriodId());
            maps.put(hashKey, bean);
        }

        return maps;
    }

    /**
     * 获取某考生的 某考点某时段的“预约记录”缓存
     */
    public ApplyRecordCacheBean getStudentApplyRecord(Long studentId, Long examSiteId, Long timePeriodId) {
        String cacheKey = String.format(CACHE_STUDENT_APPLY_RECORD, studentId);
        String hashKey = String.format("%s_%s", examSiteId, timePeriodId);
        return redisClient.getForHash(cacheKey, hashKey, ApplyRecordCacheBean.class);
    }

    /**
     * 保存某考生的 某考点某时段的“预约记录”缓存
     */
    public void saveStudentApplyRecord(ApplyRecordCacheBean value) {
        // String cacheKey = String.format(CACHE_STUDENT_APPLY_RECORD, value.getStudentId());
        // String hashKey = String.format("%s_%s", value.getExamSiteId(), value.getTimePeriodId());
        // redisClient.setForHash(cacheKey, hashKey, value);
        // log.info("SET cacheKey:{} hashKey:{} cancel:{}", cacheKey, hashKey, value.getCancel());
    }

    /**
     * 推送至考生预约记录队列
     */
    public void pushStudentApplyRecordQueue(ApplyRecordCacheBean value) {
        // if (value == null) {
        //     return;
        // }
        //
        // RQueue<ApplyRecordCacheBean> queue = redisClient.getRedissonClient()
        //         .getQueue(QUEUE_STUDENT_APPLY_RECORD);
        //
        // boolean success = queue.offer(value);
        // log.info("{}_{}_{}_{} offerQueue:{}", value.getStudentId(), value.getExamSiteId(),
        //         value.getTimePeriodId(), value.getCancel(), success);
        // if (!success) {
        //     throw new RuntimeException("推送至考生预约记录队列失败");
        // }
    }

    /**
     * 获取考生预约记录队列数量
     */
    public int getStudentApplyRecordQueueSize() {
        RQueue<ApplyRecordCacheBean> queue = redisClient.getRedissonClient()
                .getQueue(QUEUE_STUDENT_APPLY_RECORD);
        return queue.size();
    }

}
