package net.educoder.ecsonar.services;

import net.educoder.ecsonar.exception.BusinessException;
import net.educoder.ecsonar.dao.TaskInfoDao;
import net.educoder.ecsonar.dao.TaskInfoDetailDao;
import net.educoder.ecsonar.model.Metrics;
import net.educoder.ecsonar.model.RollPage;
import net.educoder.ecsonar.model.TaskInfo;
import net.educoder.ecsonar.model.TaskInfoDetail;
import net.educoder.ecsonar.model.api.Quality;
import net.educoder.ecsonar.model.api.QualityInspect;
import net.educoder.ecsonar.model.api.QualityInspectIsCompleted;
import net.educoder.ecsonar.model.api.QualityInspectResultData;
import net.educoder.ecsonar.model.vo.QualityInspectUserDataVO;
import net.educoder.ecsonar.model.vo.QualityInspectVO;
import net.educoder.ecsonar.task.QualityInspectRunnable;
import net.educoder.ecsonar.utils.IdUtils;
import net.educoder.ecsonar.utils.SystemUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

/**
 * @Author: youys
 * @Date: 2022/1/17
 * @Description:
 */
@Service
public class QualityInspectService {

    private static final Logger LOGGER = LoggerFactory.getLogger(QualityInspectService.class);

    @Autowired
    private TaskInfoDao taskInfoDao;

    @Autowired
    private TaskInfoDetailDao taskInfoDetailDao;

    @Autowired
    private DbOperateService dbOperateService;

    @Autowired
    private SonarService sonarService;

    @Autowired
    private ReportService reportService;


    @Autowired
    @Qualifier("sonarScannerPool")
    private ExecutorService sonarScannerPool;

    @Autowired
    @Qualifier("sonarQueryResultPool")
    private ExecutorService sonarQueryResultPool;


    public QualityInspect qualityInspect(QualityInspectVO qualityInspectVO) {

        List<QualityInspectUserDataVO> userDatas = qualityInspectVO.getUserDatas();
        LOGGER.info("homeworkId:{}，{}个人提交了代码", qualityInspectVO.getHomeworkId(), userDatas.size());

        if (CollectionUtils.isEmpty(userDatas)) {
            throw new BusinessException(2, "用户数据不能为空");
        }

        TaskInfo taskInfo = new TaskInfo();
        taskInfo.setId(IdUtils.nextStrId());
        taskInfo.setHomeworkId(qualityInspectVO.getHomeworkId());
        taskInfo.setLanguage(qualityInspectVO.getLanguage());
        taskInfo.setPeopleNumber(qualityInspectVO.getUserDatas().size());

        taskInfoDao.insertTaskInfo(taskInfo);

        for (QualityInspectUserDataVO userData : userDatas) {

            TaskInfoDetail taskInfoDetail = new TaskInfoDetail();
            taskInfoDetail.setId(IdUtils.nextStrId());
            taskInfoDetail.setTaskId(taskInfo.getId());
            taskInfoDetail.setProjectName(String.format("%s-%s", qualityInspectVO.getHomeworkId(), userData.getStudentId()));
            taskInfoDetail.setName(userData.getName());
            taskInfoDetail.setStudentId(userData.getStudentId());
            taskInfoDetail.setUserId(userData.getUserId());
            taskInfoDetail.setCodeIds(userData.getCodeIds().toString());

            taskInfoDetailDao.insertTaskInfoDetail(taskInfoDetail);

            QualityInspectRunnable runnable = new QualityInspectRunnable(taskInfo.getId(), qualityInspectVO.getLanguage(),
                    qualityInspectVO.getHomeworkId(), taskInfoDetail, userData, sonarQueryResultPool);
            runnable.setDbOperateService(dbOperateService);
            runnable.setSonarService(sonarService);

            // 提交任务
            sonarScannerPool.execute(runnable);
        }
        // 提交之后返回taskId
        return new QualityInspect(taskInfo.getId());
    }


    public RollPage<QualityInspectResultData> qualityInspectResultQuery(Integer pageNum, Integer pageSize, String taskId) {
        RollPage<QualityInspectResultData> rollPage = new RollPage();
        rollPage.setCurrentPage(pageNum);
        rollPage.setPageSize(pageSize);


        int offset = (pageNum - 1) * pageSize;
        Integer totalCount = taskInfoDetailDao.selectTaskInfoDetailPageCount(taskId, pageSize, offset);
        rollPage.setRecordSum(totalCount);


        if (totalCount == null || totalCount == 0) {
            rollPage.setRecordList(new ArrayList<>());
        } else {
            List<TaskInfoDetail> taskInfoDetails = taskInfoDetailDao.selectTaskInfoDetailPageList(taskId, pageSize, offset);

            List<QualityInspectResultData> resultDataList = new ArrayList<>(taskInfoDetails.size());
            rollPage.setRecordList(resultDataList);

            // 组装数据
            for (TaskInfoDetail taskInfoDetail : taskInfoDetails) {
                QualityInspectResultData resultData = new QualityInspectResultData();
                resultData.setName(taskInfoDetail.getName());
                resultData.setStudentId(taskInfoDetail.getStudentId());
                resultData.setUserId(taskInfoDetail.getUserId());

                Metrics metrics = reportService.getMetrics(taskInfoDetail.getProjectName());

                resultData.setDiscern(!metrics.getBugs().equals("-"));
                resultData.setBug(new Quality(metrics.getBlock_bugs(), metrics.getMajor_bugs(), metrics.getMinor_bugs(), metrics.getCritical_bugs(), metrics.getBugs()));
                resultData.setVulnerability(new Quality(metrics.getBlock_violations(), metrics.getMajor_violations(), metrics.getMinor_violations(), metrics.getCritical_violations(), metrics.getViolations()));
                resultData.setCodeSmall(new Quality(metrics.getBlock_code_smells(), metrics.getMajor_code_smells(), metrics.getMinor_code_smells(), metrics.getCritical_code_smells(), metrics.getCode_smells()));

                resultData.setComplexity(metrics.getComplexity());
                resultData.setTotalRowNumber(metrics.getLines());
                resultData.setQualityScore(calcQualityScore(resultData));

                resultDataList.add(resultData);
            }
        }

        return rollPage;
    }

    public QualityInspectIsCompleted qualityInspectIsCompleted(String taskId) {
        TaskInfo taskInfo = taskInfoDao.selectById(taskId);
        if (taskInfo == null) {
            throw new BusinessException(2, "任务id不存在");
        }

        if (taskInfo.getStatus() == 1) {
            return new QualityInspectIsCompleted(taskId, taskInfo.getStatus());
        }

        Integer taskNumber = taskInfoDetailDao.selectCountByTaskId(taskInfo.getId());
        int completed = taskInfo.getPeopleNumber().equals(taskNumber) ? 1 : 0;

        // 数据库未更新状态，但sonar任务已经全部执行完，更新task_info状态
        if (taskInfo.getStatus() == 0 && completed == 1) {
            taskInfoDao.updateTaskInfoStatus(taskInfo.getId(), 1);

            // 并且删除文件 /tmp/$taskId  /tmp/homeworkId/*
            String command = String.format("rm -rf /tmp/%s && rm -rf /tmp/%s/*", taskId, taskInfo.getHomeworkId());
            SystemUtil.executeAndGetExitStatus(command);
        }

        QualityInspectIsCompleted qualityInspectIsCompleted = new QualityInspectIsCompleted(taskId, completed);
        return qualityInspectIsCompleted;
    }


    /**
     * 阻断	严重	主要	次要
     * 10	5	    3	     1
     *
     * @param resultData
     * @return
     */
    private BigDecimal calcQualityScore(QualityInspectResultData resultData) {

        if (resultData.getTotalRowNumber() <= 0) {
            return null;
        }

        // 100 -（阻断 * 10 + 严重 * 5 + 主要* 3 + 次要 * 1）/ 总行数 * 100
        Quality bug = resultData.getBug();
        BigDecimal bugScore = BigDecimal.valueOf(100).subtract(BigDecimal.valueOf(bug.getBlocker() * 10 + bug.getCritical() * 5 + bug.getMajor() * 3 + bug.getMinor() * 1).divide(BigDecimal.valueOf(resultData.getTotalRowNumber()),2,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));

        Quality vulnerability = resultData.getVulnerability();
        BigDecimal vulnerabilityScore = BigDecimal.valueOf(100).subtract(BigDecimal.valueOf(vulnerability.getBlocker() * 10 + vulnerability.getCritical() * 5 + vulnerability.getMajor() * 3 + vulnerability.getMinor() * 1).divide(BigDecimal.valueOf(resultData.getTotalRowNumber()),2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));

        Quality codeSmall = resultData.getCodeSmall();
        BigDecimal codeSmallScore = BigDecimal.valueOf(100).subtract(BigDecimal.valueOf(codeSmall.getBlocker() * 10 + codeSmall.getCritical() * 5 + codeSmall.getMajor() * 3 + codeSmall.getMinor() * 1).divide(BigDecimal.valueOf(resultData.getTotalRowNumber()),2,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));


        // 总得分= bugScore * 0.5 + vulnerabilityScore * 0.3 + codeSmallScore * 0.2
        BigDecimal score = bugScore.multiply(BigDecimal.valueOf(0.5)).add(vulnerabilityScore.multiply(BigDecimal.valueOf(0.3))).add(codeSmallScore.multiply(BigDecimal.valueOf(0.2))).setScale(2, RoundingMode.HALF_UP);
        return score;
    }
}
