package cd.casic.ci.process.engine.worker.sast;


import cd.casic.ci.process.common.WorkAtom;
import cd.casic.ci.process.dto.req.sast.SastApplicationCreateReq;
import cd.casic.ci.process.dto.req.sast.SastReportCreateReq;
import cd.casic.ci.process.dto.req.sast.SastVulInfoReq;
import cd.casic.ci.process.dto.resp.sast.*;
import cd.casic.ci.process.engine.constant.SastConstant;
import cd.casic.ci.process.engine.postHandler.ExecuteTaskPostHandler;
import cd.casic.ci.process.engine.runContext.TaskRunContext;
import cd.casic.ci.process.engine.worker.base.BaseWorker;
import cd.casic.ci.process.process.converter.SastConverter;
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
import cd.casic.ci.process.process.dataObject.history.PipPipelineHisInstance;
import cd.casic.ci.process.process.dataObject.pipeline.PipPipeline;
import cd.casic.ci.process.process.dataObject.target.TargetVersion;
import cd.casic.ci.process.process.dataObject.task.PipTask;
import cd.casic.ci.process.process.service.pipeline.PipelineService;
import cd.casic.ci.process.process.service.sast.SastService;
import cd.casic.ci.process.process.service.target.TargetVersionService;
import cd.casic.ci.process.process.service.task.TaskService;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * sast
 * */
@Slf4j
@WorkAtom(taskType = "SAST")
public class SastWorker extends BaseWorker {
    @Resource
    private SastService sastService;
    @Resource
    private SastConverter converter;
    @Resource
    private TaskService taskService;
    @Resource
    private PipelineService pipelineService;
    @Resource
    private TargetVersionService targetVersionService;
    @Override
    public void execute(TaskRunContext context) {
        PipBaseElement contextDef = context.getContextDef();
        String id = contextDef.getId();
        log.info("==============触发worker执行========");
        log.info("==========运行context：{}===========", JSON.toJSONString(context));
        if (contextDef instanceof PipTask task) {
            String applicationId = (String)(task.getTaskProperties().get(SastConstant.APPLICATION_ID));
            if (StringUtils.isEmpty(applicationId)) {
                append(context,"未获取到sast信息");
                toBadEnding();
            }
            SastApplicationStatusResp applicationStatus = sastService.getApplicationStatus(applicationId);
            SastApplicationEchoResp applicationEcho = sastService.getApplicationEcho(applicationId);
            append(context,"获取到sast任务配置"+JSON.toJSONString(applicationEcho));
            if (SastConstant.STATUS_PROGRESSING.equals(applicationStatus.getJobStatus())||SastConstant.STATUS_PENDING.equals(applicationStatus.getJobStatus())) {
                append(context,"当前任务正在运行中");
//                toBadEnding();
            } else{
//                SastApplicationCreateReq sastApplicationCreateReq = converter.converter(applicationEcho);
//                List<String> list = applicationEcho.getLodeFiles().stream().map(SastApplicationEchoResp.LodeFile::getFileId).toList();
//                sastApplicationCreateReq.setFileId(list);
//                append(context,"开始启动SAST任务,检测文件信息：{}"+JSON.toJSONString(applicationEcho.getLodeFiles()));
//                log.info("SAST启动任务入参:{}",JSON.toJSONString(sastApplicationCreateReq));
                SastApplicationStashResp sastApplicationCreateResp = sastService.stashScan(applicationId);
                log.info("SAST启动任务返回值:{}",JSON.toJSONString(sastApplicationCreateResp));
                append(context,"启动任务完毕");
            }
//             检测状态是否完毕
            applicationStatus = sastService.getApplicationStatus(applicationId);
            List<SastEngineLogResp> oldLogList = new ArrayList<>();
            int repeat = 0;
            while (!SastConstant.STATUS_JOB_DONE.equals(applicationStatus.getJobStatus())) {
                try {
                    Thread.sleep(20000L);
                } catch (InterruptedException e) {
                    log.error("SAST WORKER线程中断");
                }
                try {
                    applicationStatus=sastService.getApplicationStatus(applicationId);
                } catch (Exception e) {
                    log.error("sastWorker执行失败",e);
                    append(context,"获取状态失败");
                    if (repeat++>3) {
                        append(context,"获取状态失败,尝试超过三次执行失败");
                        toBadEnding();
                    }
                    continue;
                }
                append(context,"当前SAST运行中，运行状态："+applicationStatus.getJobStatus());
                if (SastConstant.STATUS_CANCEL.equals(applicationStatus.getJobStatus())) {
                    append(context,"任务被取消");
                    toBadEnding();
                }
                List<SastEngineLogResp> logList = sastService.engineLog(applicationId);
                if (logList.size()>oldLogList.size()) {
                    for (int i = oldLogList.size(); i < logList.size(); i++) {
                        SastEngineLogResp logResp = logList.get(i);
                        append(context,logResp.getPrettyPrint());
                    }
                    oldLogList = logList;
                }
                repeat = 0;
            }
            JSONObject reportJSON = getReportJSON(applicationId, SastConstant.REPORT_MODE_DETAILS,context);
            String reportUrl = getReportUrl(applicationId, SastConstant.REPORT_MODE_DETAILS);
            task.getTaskProperties().put(SastConstant.REPORT_JSON,reportJSON);
            task.getTaskProperties().put(SastConstant.REPORT_URL,reportUrl);
            taskService.updateById(task);
        }
    }
    private JSONObject getReportJSON(String applicationId,String model,TaskRunContext context){
        String reqJSON = "{\"mode\":\"DETAILS\",\"contacts\":\"admin@clouditera.com\",\"reportType\":\"APPLICATION\",\"format\":\"JSON\",\"projectId\":\"9e57b627-2297-4bbf-a08a-92ff7d6b719d\",\"applicationIds\":[\"9e3e55d6-6d28-4fd1-adc2-15b6cf0702db\"],\"bugLevels\":[\"DEADLY\",\"SEVERITY\",\"POOR_RISK\",\"LOW_RISK\"],\"audRiskLevels\":[\"WAITING\",\"IGNORE\",\"HIGH\",\"MEDIUM\",\"LOW\",\"NONE\"],\"applicationScope\":\"ALL\"}";
        SastReportCreateReq sastReportCreateReq = JSON.parseObject(reqJSON, SastReportCreateReq.class);
        sastReportCreateReq.setMode(model);
        sastReportCreateReq.setProjectId("8a863857-bab3-40d9-a79a-2cb6617a9dd3");
        sastReportCreateReq.setFormat(SastConstant.REPORT_FORMAT_JSON);
        sastReportCreateReq.setApplicationIds(Collections.singletonList(applicationId));
        log.info("获取JSON报告请求参数{}",JSON.toJSONString(sastReportCreateReq));
        String reportId = sastService.reportIndex(sastReportCreateReq);
        log.info("获取JSON报告获得reportId{}",reportId);
        // todo 获取报告状态
        SastReportStatusResp reportStatus = sastService.getReportStatus(reportId);
        try {
            while (reportStatus.getStatus().equals("WAITING")||reportStatus.getStatus().equals("GENERATING")){
                reportStatus = sastService.getReportStatus(reportId);
                append(context,"当前生成报告状态为:"+reportStatus.getStatus());
                Thread.sleep(1000L);
            }
        } catch (Exception e) {
            throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"获取sast报告状态失败");
        }
        if (reportStatus.getStatus().equals("FAIL")) {
            throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"生成sast报告失败");
        }
        JSONObject reportJson = getJSONString(reportId);
        PipTask task = context.getContextDef() instanceof PipTask ? ((PipTask) context.getContextDef()) : null;
        PipPipeline pipeline = pipelineService.getById(task.getPipelineId());
        String targetVersionId = pipeline.getTargetVersionId();
        TargetVersion targetVersion = targetVersionService.getById(pipeline.getTargetVersionId());
        postHandlerManager.registerPostHandler(new ExecuteTaskPostHandler(task.getId(),task.getPipelineId()) {
            @Override
            public void executeAfterDone(PipPipelineHisInstance pipPipelineHisInstance) {
                JSONObject defects = reportJson.getJSONObject("defects");
                for (String key : defects.keySet()) {
                    String string = defects.getString(key);
                    List<SastVulInfoReq> sastVulInfoReqs = JSONArray.parseArray(string, SastVulInfoReq.class);
                    sastService.saveReportVulInfo(sastVulInfoReqs,pipeline.getTargetType(),targetVersion.getFileName(),"重庆市",pipPipelineHisInstance.getId(),taskId,task.getTaskType());
                }
            }
        });

        JSONObject jsonObject = reportJson.getJSONObject("task_summary");
        JSONObject severity = jsonObject.getJSONObject("defect_severity_distribution");
        JSONObject rule = jsonObject.getJSONObject("detection_rule_distribution");
        JSONObject res = new JSONObject();
        res.put("defect_severity_distribution",severity);
        res.put("detection_rule_distribution",rule);
        return res;
    }
    private String getReportUrl(String applicationId,String model){
        String reqJSON = "{\"mode\":\"DETAILS\",\"contacts\":\"admin@clouditera.com\",\"reportType\":\"APPLICATION\",\"format\":\"JSON\",\"projectId\":\"8a863857-bab3-40d9-a79a-2cb6617a9dd3\",\"applicationIds\":[\"9e3e55d6-6d28-4fd1-adc2-15b6cf0702db\"],\"bugLevels\":[\"DEADLY\",\"SEVERITY\",\"POOR_RISK\",\"LOW_RISK\"],\"audRiskLevels\":[\"WAITING\",\"IGNORE\",\"HIGH\",\"MEDIUM\",\"LOW\",\"NONE\"],\"applicationScope\":\"ALL\"}";
        SastReportCreateReq sastReportCreateReq = JSON.parseObject(reqJSON, SastReportCreateReq.class);
        sastReportCreateReq.setMode(model);
        sastReportCreateReq.setProjectId("8a863857-bab3-40d9-a79a-2cb6617a9dd3");
        sastReportCreateReq.setFormat(SastConstant.REPORT_FORMAT_PDF);
        sastReportCreateReq.setApplicationIds(Collections.singletonList(applicationId));
        log.info("获取报告请求参数{}",JSON.toJSONString(sastReportCreateReq));
        return sastService.reportDownload(sastService.reportIndex(sastReportCreateReq));
    }
    private JSONObject getJSONString(String reportId){
        return sastService.jsonReportDownload(reportId);
    }
}
