package com.qmth.exam.reserve.job;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.qmth.boot.core.concurrent.service.ConcurrentService;
import com.qmth.exam.reserve.bean.apply.ApplyRecordCacheBean;
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.enums.EventType;
import com.qmth.exam.reserve.service.OperateLogService;
import com.qmth.exam.reserve.service.StudentApplyService;
import com.qmth.exam.reserve.util.JsonHelper;
import org.redisson.api.RLock;
import org.redisson.api.RQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class StudentApplyRecordJob {

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

    @Autowired
    private StudentApplyService studentApplyService;

    @Autowired
    private OperateLogService operateLogService;

    @Autowired
    private ConcurrentService concurrentService;

    @Autowired
    private RedisClient redisClient;

    /**
     * 定时将“考生预约记录队列”中的数据保存至数据库
     * 注：每N秒执行一次
     */
    // @Scheduled(fixedDelay = 10000, initialDelay = 30000)
    public void saveStudentApplyRecordJob() {
        long start = System.currentTimeMillis();

        RLock curLock = (RLock) concurrentService.getLock(CacheConstants.LOCK_STUDENT_APPLY_RECORD);
        try {
            if (!curLock.tryLock()) {
                log.info("[JOB] locking...");
                return;
            }

            RQueue<ApplyRecordCacheBean> queue = redisClient.getRedissonClient()
                    .getQueue(CacheConstants.QUEUE_STUDENT_APPLY_RECORD);

            int queueSize = queue.size();
            log.info("[JOB] queue size:{}", queueSize);

            for (int i = 1; i <= queueSize; i++) {
                ApplyRecordCacheBean value = queue.poll();

                try {
                    this.saveOrUpdate(value, i);
                } catch (Exception e) {
                    // 保存至数据库失败，放回队列重试执行
                    boolean success = queue.offer(value);
                    log.error("[JOB] offerQueue:{} studentId:{} err:{}", success, value.getStudentId(), e.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("[JOB] err:{}", e.getMessage(), e);
        } finally {
            try {
                // 解锁前检查当前线程是否持有该锁
                if (curLock.isLocked() && curLock.isHeldByCurrentThread()) {
                    curLock.unlock();
                }
            } catch (Exception e) {
                // ignore
            }

            long end = System.currentTimeMillis();
            log.info("[JOB] cost:{}ms", end - start);
        }
    }

    // @Transactional
    public void saveOrUpdate(ApplyRecordCacheBean bean, int index) {
        if (bean == null) {
            return;
        }

        LambdaQueryWrapper<StudentApplyEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StudentApplyEntity::getExamSiteId, bean.getExamSiteId());
        wrapper.eq(StudentApplyEntity::getTimePeriodId, bean.getTimePeriodId());
        wrapper.eq(StudentApplyEntity::getStudentId, bean.getStudentId());
        StudentApplyEntity data = studentApplyService.getOne(wrapper);

        String msg = String.format("%s_%s_%s_%s", bean.getStudentId(), bean.getExamSiteId(),
                bean.getTimePeriodId(), bean.getCancel());
        if (data != null) {
            // 存在预约记录，则修改
            data.setCancel(bean.getCancel());
            data.setOperateId(bean.getOperateId());
            data.setUpdateTime(bean.getOperateTime());

            LambdaUpdateWrapper<StudentApplyEntity> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(StudentApplyEntity::getCancel, bean.getCancel());
            updateWrapper.set(StudentApplyEntity::getOperateId, bean.getOperateId());
            updateWrapper.set(StudentApplyEntity::getUpdateTime, bean.getOperateTime());
            updateWrapper.eq(StudentApplyEntity::getId, data.getId());
            studentApplyService.update(updateWrapper);
            log.info("{} 预约修改！{}", msg, index);
        } else {
            // 不存在预约记录，则新增
            data = new StudentApplyEntity();
            data.setStudentId(bean.getStudentId());
            data.setExamSiteId(bean.getExamSiteId());
            data.setTimePeriodId(bean.getTimePeriodId());
            data.setCancel(bean.getCancel());
            data.setOperateId(bean.getOperateId());
            data.setCreateTime(bean.getOperateTime());
            data.setUpdateTime(bean.getOperateTime());
            studentApplyService.save(data);
            log.info("{} 预约新增！{}", msg, index);
        }

        if (data.getCancel()) {
            // 保存“取消预约”操作记录
            operateLogService.insertOperateLog(bean.getOperateId(), EventType.CANCEL_APPLY, JsonHelper.toJson(data));
        }
    }

}
