package com.qmth.teachcloud.mark.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.mark.MarkArbitrateStatus;
import com.qmth.teachcloud.common.enums.mark.MarkMode;
import com.qmth.teachcloud.common.service.FileUploadService;
import com.qmth.teachcloud.common.service.SysUserService;
import com.qmth.teachcloud.common.util.ServletUtil;
import com.qmth.teachcloud.mark.dto.mark.manage.MarkArbitrateDto;
import com.qmth.teachcloud.mark.dto.mark.manage.MarkArbitrateMarkerDto;
import com.qmth.teachcloud.mark.dto.mark.manage.MarkArbitrateSettingDto;
import com.qmth.teachcloud.mark.dto.mark.manage.Task;
import com.qmth.teachcloud.mark.entity.*;
import com.qmth.teachcloud.mark.enums.LockType;
import com.qmth.teachcloud.mark.lock.LockService;
import com.qmth.teachcloud.mark.mapper.MarkArbitrateHistoryMapper;
import com.qmth.teachcloud.mark.params.MarkResult;
import com.qmth.teachcloud.mark.service.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * <p>
 * 仲裁记录表 服务实现类
 * </p>
 *
 * @author xf
 * @since 2023-09-22
 */
@Service
public class MarkArbitrateHistoryServiceImpl extends ServiceImpl<MarkArbitrateHistoryMapper, MarkArbitrateHistory> implements MarkArbitrateHistoryService {

    // 并发处理互斥锁
    private Map<Long, Integer> currentTaskMap = new HashMap<>();

    @Resource
    private MarkPaperService markPaperService;
    @Resource
    private MarkQuestionService markQuestionService;
    @Resource
    private MarkGroupService markGroupService;
    @Resource
    private MarkStudentService markStudentService;
    @Resource
    private MarkTaskService markTaskService;
    @Resource
    private MarkUserGroupService markUserGroupService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private TaskService taskService;
    @Resource
    private FileUploadService fileUploadService;
    @Resource
    private MarkService markService;
    @Resource
    private LockService lockService;

    @Override
    public IPage<MarkArbitrateDto> pageArbitrate(Long examId, String paperNumber, Integer groupNumber, String status, String className, Integer pageNumber, Integer pageSize) {
        Page<MarkArbitrateDto> page = new Page<>(pageNumber, pageSize);
        IPage<MarkArbitrateDto> markArbitrateDtoIPage = this.baseMapper.pageArbitrate(page, examId, paperNumber, groupNumber, status, className);
        for (MarkArbitrateDto arbitrateDto : markArbitrateDtoIPage.getRecords()) {
            if (arbitrateDto.getStudentId() != null) {
                MarkStudent markStudent = markStudentService.getById(arbitrateDto.getStudentId());
                if (markStudent != null) {
                    arbitrateDto.setStudentCode(markStudent.getStudentCode());
                    arbitrateDto.setStudentName(markStudent.getStudentName());
                }
            }
            arbitrateDto.setStatusName(arbitrateDto.getStatus().getName());
            arbitrateDto.setGroupQuestions(markQuestionService.assembleGroupQuestionsByExamIdAndPaperNumberAndNumber(examId, paperNumber, arbitrateDto.getGroupNumber()));
        }
        return markArbitrateDtoIPage;
    }

    @Override
    public void clearArbitrate(Long arbitrateId) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        if (arbitrateId != null) {
            releaseTask(arbitrateId);
        } else {
            releaseByUser(sysUser.getId());
        }
    }

    @Override
    public List<MarkArbitrateMarkerDto> listMarkerArbitrateRecord(Long arbitrateId) {
        MarkArbitrateHistory markArbitrateHistory = this.getById(arbitrateId);
        List<MarkTask> markTaskList = markTaskService.listByExamIdAndPaperNumberAndGroupNumberAndStudentId(markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(), markArbitrateHistory.getGroupNumber(), markArbitrateHistory.getStudentId());
        List<MarkArbitrateMarkerDto> markArbitrateMarkerDtoList = new ArrayList<>();
        for (MarkTask markTask : markTaskList) {
            MarkArbitrateMarkerDto markArbitrateMarkerDto = new MarkArbitrateMarkerDto();
            MarkUserGroup markUserGroup = markUserGroupService.getByExamIdAndPaperNumberAndGroupNumberAndUserId(markTask.getExamId(), markTask.getPaperNumber(), markTask.getGroupNumber(), markTask.getUserId());
            if (markUserGroup != null) {
                markArbitrateMarkerDto.setUserId(markTask.getUserId());
                SysUser sysUser = sysUserService.getById(markTask.getUserId());
                if (sysUser != null) {
                    markArbitrateMarkerDto.setUserLoginName(sysUser.getLoginName());
                    markArbitrateMarkerDto.setUserName(sysUser.getRealName());
                }
                markArbitrateMarkerDto.setMarkTime(markTask.getMarkerTime());
                markArbitrateMarkerDto.setScoreList(markTask.getMarkerScoreList());
                markArbitrateMarkerDto.setTotalScore(markTask.getMarkerScore());
            }
            markArbitrateMarkerDtoList.add(markArbitrateMarkerDto);
        }
        return markArbitrateMarkerDtoList;
    }

    @Override
    public MarkArbitrateSettingDto getArbitrateSetting(Long arbitrateId, Long examId, String paperNumber, Integer groupNumber) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        MarkArbitrateHistory markArbitrateHistory;
        if (arbitrateId != null) {
            markArbitrateHistory = this.getById(arbitrateId);
            if (markArbitrateHistory == null) {
                throw ExceptionResultEnum.ERROR.exception("仲裁数据不存在");
            }
            examId = markArbitrateHistory.getExamId();
            paperNumber = markArbitrateHistory.getPaperNumber();
        } else {
            if (examId == null || StringUtils.isBlank(paperNumber) || groupNumber == null) {
                throw ExceptionResultEnum.ERROR.exception("参数有误");
            }
        }

        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
        MarkArbitrateSettingDto markArbitrateSettingDto = new MarkArbitrateSettingDto();
        if (markPaper != null) {
            markArbitrateSettingDto.setMarkMode(MarkMode.UNLIMITED.equals(markPaper.getMarkMode()) ? MarkMode.TRACK.name() : markPaper.getMarkMode().name());
            markArbitrateSettingDto.setCourseCode(markPaper.getCourseCode());
            markArbitrateSettingDto.setCourseName(markPaper.getCourseName());
            markArbitrateSettingDto.setPaperNumber(markPaper.getPaperNumber());
            markArbitrateSettingDto.setUserLoginName(sysUser.getLoginName());
            markArbitrateSettingDto.setUserName(sysUser.getRealName());
            if (StringUtils.isNotBlank(markPaper.getPaperFilePath())) {
                markArbitrateSettingDto.setPaperUrl(fileUploadService.filePreview(markPaper.getPaperFilePath()));
            }
            if (StringUtils.isNotBlank(markPaper.getAnswerFilePath())) {
                markArbitrateSettingDto.setAnswerUrl(fileUploadService.filePreview(markPaper.getAnswerFilePath()));
            }
        }
        return markArbitrateSettingDto;
    }

    @Override
    public Task getArbitrateTask(Long arbitrateId, Long examId, String paperNumber, Integer groupNumber) {
        MarkArbitrateHistory markArbitrateHistory;
        if (arbitrateId != null) {
            markArbitrateHistory = this.getById(arbitrateId);
            if (MarkArbitrateStatus.MARKED.equals(markArbitrateHistory.getStatus())) {
                throw ExceptionResultEnum.ERROR.exception("数据已处理，请刷新数据列表");
            }
        } else {
            if (examId == null || StringUtils.isBlank(paperNumber) || groupNumber == null) {
                throw ExceptionResultEnum.ERROR.exception("参数有误");
            }
            markArbitrateHistory = this.baseMapper.getArbitrateWaitingOne(examId, paperNumber, groupNumber);
        }
        if (markArbitrateHistory == null) {
            return null;
        }
        MarkGroup markGroup = markGroupService.getByExamIdAndPaperNumberAndGroupNumber(markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(), markArbitrateHistory.getGroupNumber());
        return taskService.build(markArbitrateHistory, markGroup);
    }

    @Override
    public void saveArbitrateTask(MarkResult markResult) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        MarkArbitrateHistory markArbitrateHistory = this.getById(markResult.getTaskId());
        if (markArbitrateHistory != null) {
            try {
                lockService.watch(LockType.EXAM_SUBJECT, markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber());
                lockService.watch(LockType.GROUP, markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(), markArbitrateHistory.getGroupNumber());
                markService.processArbitrate(markResult, sysUser.getId());
                releaseTask(markArbitrateHistory.getId());
            } catch (Exception e) {
                log.error("ArbitrateController-处理仲裁卷出错", e);
            } finally {
                lockService.unwatch(LockType.GROUP, markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber(), markArbitrateHistory.getGroupNumber());
                lockService.unwatch(LockType.EXAM_SUBJECT, markArbitrateHistory.getExamId(), markArbitrateHistory.getPaperNumber());
            }
        } else {
            throw ExceptionResultEnum.ERROR.exception("任务不存在");
        }
    }

    @Override
    public void deleteByStudentId(Long studentId) {
        UpdateWrapper<MarkArbitrateHistory> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(MarkArbitrateHistory::getStudentId, studentId);
        this.remove(updateWrapper);
    }

    @Override
    public void deleteByExamIdAndPaperNumberAndGroupNumber(Long examId, String paperNumber, Integer groupNumber) {
        UpdateWrapper<MarkArbitrateHistory> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(MarkArbitrateHistory::getExamId, examId)
                .eq(MarkArbitrateHistory::getPaperNumber, paperNumber)
                .eq(MarkArbitrateHistory::getGroupNumber, groupNumber);
        this.remove(updateWrapper);
    }

    @Override
    public JSONObject getArbitrateStatus(Long examId, String paperNumber, Integer groupNumber) {
        Long userId = ServletUtil.getRequestUserId();
        QueryWrapper<MarkArbitrateHistory> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MarkArbitrateHistory::getExamId, examId)
                .eq(MarkArbitrateHistory::getPaperNumber, paperNumber)
                .eq(MarkArbitrateHistory::getGroupNumber, groupNumber);
        List<MarkArbitrateHistory> markArbitrateHistoryList = this.list(queryWrapper);
        JSONObject jsonObject = new JSONObject();
        if (CollectionUtils.isEmpty(markArbitrateHistoryList)) {
            jsonObject.put("valid", false);
            return jsonObject;
        }

        long totalCount = markArbitrateHistoryList.stream().filter(m -> MarkArbitrateStatus.WAITING.equals(m.getStatus())).count();
        long markedCount = markArbitrateHistoryList.stream().filter(m -> MarkArbitrateStatus.MARKED.equals(m.getStatus()) && userId.equals(m.getUpdateUserId())).count();

        jsonObject.put("totalCount", totalCount);
        jsonObject.put("markedCount", markedCount);
        jsonObject.put("valid", (totalCount + markedCount) > 0);
        return jsonObject;
    }

    @Override
    public List<Task> getHistory(Long examId, String paperNumber, Integer groupNumber, Integer pageNumber, Integer pageSize) {
        Long userId = ServletUtil.getRequestUserId();
        Page<MarkArbitrateHistory> page = new Page<>(pageNumber, pageSize);
        List<MarkArbitrateHistory> markArbitrateHistoryList = this.baseMapper.getHistory(page, examId, paperNumber, groupNumber, MarkArbitrateStatus.MARKED.name(), userId);
        List<Task> taskList = new ArrayList<>();
        MarkGroup markGroup = markGroupService.getByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, groupNumber);
        for (MarkArbitrateHistory history : markArbitrateHistoryList) {
            Task task = taskService.build(history, markGroup);
            task.setPrevious(true);
            taskList.add(task);
        }
        return taskList;
    }

    @Override
    public Integer waitArbitrateCount(Long examId, String paperNumber, Integer groupNumber, String className) {
        return this.baseMapper.waitArbitrateCount(examId, paperNumber, groupNumber, MarkArbitrateStatus.WAITING.name(), className);
    }

    private void releaseTask(Long taskId) {
        synchronized (currentTaskMap) {
            currentTaskMap.remove(taskId);
        }
    }

    private void releaseByUser(Long userId) {
        Set<Long> taskIds = new HashSet<>();
        taskIds.addAll(currentTaskMap.keySet());
        synchronized (currentTaskMap) {
            for (Long taskId : taskIds) {
                Integer value = currentTaskMap.get(taskId);
                if (value != null && value.equals(userId)) {
                    currentTaskMap.remove(taskId);
                }
            }
        }
    }
}
