package net.educoder.ecsonar.services;


import net.educoder.ecsonar.constant.Constant;
import net.educoder.ecsonar.model.SonarScannerParam;
import net.educoder.ecsonar.model.api.Person;
import net.educoder.ecsonar.model.api.SonarRequest;
import net.educoder.ecsonar.task.ReadExcelRunnable;
import net.educoder.ecsonar.utils.ExcelUtil;
import net.educoder.ecsonar.utils.ExcelUtils;
import net.educoder.ecsonar.utils.SystemUtil;
import net.educoder.ecsonar.utils.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class SonarService {

    private Logger log = LoggerFactory.getLogger(getClass());


    @Value("${sonar.url}")
    String sonarUrl;

//    @Value("${sonar.token}")
//    String sonarToken;

    @Value("${extract.path}")
    String extractProgramPath;

    @Autowired
    private ExecutorService executorService;

    private ConcurrentHashMap<String, Consumer<SonarScannerParam>> concurrentHashMap = new ConcurrentHashMap(8);

    @PostConstruct
    public void init() {
        concurrentHashMap.put(Constant.JAVA, param -> {
            String command = "sonar-scanner " +
                    "-Dsonar.host.url=" + sonarUrl + " " +
                    "-Dsonar.sourceEncoding=utf-8 " +
                    "-Dsonar.projectKey=" + param.getProjectKey() + " " +
                    "-Dsonar.java.binaries=./ " +
                    "-Dsonar.projectBaseDir=" + param.getProjectPath();

            SystemUtil.ExecuteResp executeResp = SystemUtil.executeAndGetExitStatus(command);
            log.info("java语言:command{},result:{}",command,executeResp.getOutput());

        });
        concurrentHashMap.put(Constant.PYTHON, param -> {
            String command = "sonar-scanner " +
                    "-Dsonar.host.url=" + sonarUrl + " " +
                    "-Dsonar.sourceEncoding=utf-8 " +
                    "-Dsonar.projectKey=" + param.getProjectKey() + " " +
                    "-Dsonar.projectBaseDir=" + param.getProjectPath();

            SystemUtil.ExecuteResp executeResp = SystemUtil.executeAndGetExitStatus(command);
            log.info("python语言:command{},result:{}",command,executeResp.getOutput());
        });
        concurrentHashMap.put(Constant.C, param -> {
            String command = "cppcheck --xml --xml-version=2 --enable=all " + param.getProjectPath() + " 2> " + param.getCppCheckReportPath() +
                    "&& sonar-scanner " +
                    "-Dsonar.host.url=" + sonarUrl + " " +
                    "-Dsonar.sourceEncoding=utf-8 " +
                    "-Dsonar.projectKey=" + param.getProjectKey() + " " +
                    "-Dsonar.cxx.cppcheck.reportPath=" + param.getCppCheckReportPath() + " " +
                    "-Dsonar.projectBaseDir=" + param.getProjectPath();
            SystemUtil.ExecuteResp executeResp = SystemUtil.executeAndGetExitStatus(command);
            log.info("c语言:command{},result:{}",command,executeResp.getOutput());
        });
        concurrentHashMap.put(Constant.CXX, param -> {
            String command = "cppcheck --xml --xml-version=2 --enable=all " + param.getProjectPath() + " 2> " + param.getCppCheckReportPath() +
                    "&& sonar-scanner " +
                    "-Dsonar.host.url=" + sonarUrl + " " +
                    "-Dsonar.sourceEncoding=utf-8 " +
                    "-Dsonar.projectKey=" + param.getProjectKey() + " " +
                    "-Dsonar.cxx.cppcheck.reportPath=" + param.getCppCheckReportPath() + " " +
                    "-Dsonar.projectBaseDir=" + param.getProjectPath();
            SystemUtil.ExecuteResp executeResp = SystemUtil.executeAndGetExitStatus(command);
            log.info("c++: command:{}, result:{}", command, executeResp.getOutput());
        });
    }

    /**
     * 提交一个任务
     *
     * @param zipUrl
     */
    public String scan(String zipSavePath, String zipUrl, int homeworkId, String uid, int index) throws Exception {
        String key = String.format("%d-%s", homeworkId, uid);

        String[] split = StringUtils.split(zipUrl, "/");

        String zipFilename = UrlUtil.getURLDecoderString(split[split.length - 1]);
        String savePath = zipSavePath + zipFilename;

        //1. 下载文件
        if (index == 1) {
            download(zipUrl, savePath);
        }

        //2. 解压目录
        String extractPath = zipSavePath + key;
        if (index == 1) {
            log.info("savePath: {}, extractPath: {}", savePath, extractPath);
            extract(savePath, extractPath);
        }

        //3. 执行检测
        String subPath = "/" + SystemUtil.executeAndGetExitStatus("cd " + extractPath + " && ls | head -1").getOutput();
        if (index != 1) {
            subPath += "/毕业设计项目个人开发代码";
//            subPath += "/个人";
        } else {
            subPath += "/毕业设计项目完整代码";
//            subPath += "/完整";
        }

        log.info("scan zipSavePath: {}, zipFilename: {}, savePath: {}, extractPath: {}, subPath: {}", zipSavePath, zipFilename, savePath, extractPath, subPath);

        if (!new File(extractPath + subPath).exists()) {
            throw new Exception("项目文件夹不存在");
        }


        key = String.format("%s-%d", key, index);

        sonar(extractPath + subPath, key);

        return key;
    }

    /**
     * 提交一个任务
     */
    public String scanSingle(String codeSavePath, String codeUrl, int homeworkId, String uid, String key) throws Exception {

        String[] split = StringUtils.split(codeUrl, "/");

        String codeFilename = UrlUtil.getURLDecoderString(split[split.length - 1]);

        String savePath = codeSavePath + codeFilename;
        if (!new File(savePath).exists()) {
            new File(savePath).mkdir();
        }
        // 代码文件后缀写死
        String saveFile = savePath + File.separator + codeFilename + ".cpp";

        // 下载文件
        download(codeUrl, saveFile);

        sonar(savePath, key);

        return key;
    }


    public void sonar(String language, SonarScannerParam sonarScannerParam) {
        Consumer<SonarScannerParam> consumer = concurrentHashMap.get(language.toLowerCase());
        if (consumer != null) {
            log.info("语言:{},projectName:{},path:{}找到了consumer", language, sonarScannerParam.getProjectKey(),sonarScannerParam.getProjectPath());
            consumer.accept(sonarScannerParam);
        }else{
            sonar(sonarScannerParam.getProjectPath(), sonarScannerParam.getProjectKey());
        }
    }


    public void sonar(String projectPath, String key) {
        String command = "sonar-scanner " +
                "-Dsonar.host.url=" + sonarUrl + " " +
                "-Dsonar.sourceEncoding=utf-8 " +
                "-Dsonar.projectKey=" + key + " " +
                "-Dsonar.java.binaries=./ " +
                "-Dsonar.projectBaseDir=" + projectPath;
        log.info("projectPath:{},key:{}, command: {}", projectPath, key, command);
        SystemUtil.executeAndGetExitStatus(command);
    }

    /**
     * 是否是支持的语言
     * @param language
     * @return
     */
    public boolean supportLanguage(String language){
        return concurrentHashMap.containsKey(language.toLowerCase());
    }

    /**
     * 解压zip文件
     *
     * @param zipFile
     */
    private void extract(String zipFile, String extractPath) throws Exception {
        File extractFile = new File(extractPath);
        if (extractFile.exists()) {
            extractFile.delete();
        }
        extractFile.mkdir();

        SystemUtil.ExecuteResp executeResp = SystemUtil.executeAndGetExitStatus("cd "
                + extractPath
                + " && " + extractProgramPath + " -encoding GBK " + zipFile);
    }

    /**
     * 下载文件
     *
     * @param zipUrl
     */
    private void download(String zipUrl, String zipPath) throws IOException {
        log.info("下载文件: {}-{}", zipUrl, zipPath);

        String cookie = "_educoder_session=1f4bf34409fec2180b99aa1cbf0b7586";
        URL url = new URL(zipUrl);
        URLConnection conn = url.openConnection();
        conn.setRequestProperty("Cookie", cookie);
        conn.setDoInput(true);

        try (BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
             FileOutputStream fileOutputStream = new FileOutputStream(zipPath)) {
            byte dataBuffer[] = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
                fileOutputStream.write(dataBuffer, 0, bytesRead);
            }
        } catch (IOException e) {
            log.error("文件下载失败: {}-{}", zipUrl, zipPath);
            throw e;
        }

    }


    /**
     * 扫描excel文件
     *
     * @param excelPath
     */
    public void scanExcel(String excelPath) {

        int homeworkId = 20210101;

        try {
            List<Person> personList = ExcelUtil.readExcel(excelPath);

            Map<String, List<Person>> collect = personList.stream().collect(Collectors.groupingBy(Person::getUid));

            collect.forEach((k, v) -> {
                SonarRequest request = new SonarRequest();
                request.setHomeworkId(homeworkId);
                request.setPersonList(v);

                // 开始调用sonar分析代码
                executorService.execute(new ReadExcelRunnable(request, this));
            });


        } catch (Exception e) {
            e.printStackTrace();
        }


    }


}
