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


import cd.casic.ci.process.common.WorkAtom;
import cd.casic.ci.process.dto.req.report.ReportAssetTaskReq;
import cd.casic.ci.process.dto.resp.report.ReportResp;
import cd.casic.ci.process.engine.context.ConstantContextHolder;
import cd.casic.ci.process.engine.runContext.BaseRunContext;
import cd.casic.ci.process.engine.runContext.TaskRunContext;
import cd.casic.ci.process.engine.worker.base.HttpWorker;
import cd.casic.ci.process.process.dao.pipeline.PipTaskDao;
import cd.casic.ci.process.process.dataObject.base.BaseIdReq;
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
import cd.casic.ci.process.process.dataObject.task.PipTask;
import cd.casic.ci.process.process.service.report.impl.ReportServiceImpl;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import java.util.*;

/**
 * @author HopeLi
 * @version v1.0
 * @ClassName ScaSbomWorker
 * @Date: 2025/5/21 9:30
 * @Description:
 */
@Slf4j
@WorkAtom(taskType = "CODING")
public class CodingWorker extends HttpWorker {

    private static final int POLLING_INTERVAL = 5000; // 轮询间隔，单位：毫秒
    private static final int MAX_POLLING_TIMES = 100; // 最大退出次数

    public static final Integer REPO_TYPE_GIT=8;

    @Resource
    private PipTaskDao pipTaskDao;

    @Resource
    private ReportServiceImpl reportService;

    @Override
    public void execute(TaskRunContext context) {
        int statusCode = 0;
        Map<String, Object> localVariables = context.getLocalVariables();

        PipBaseElement contextDef = context.getContextDef();
        append(context,"SCA-代码仓库管理节点开始执行");
        append(context,"SCA-代码仓库管理节点配置：" + contextDef);
        if (ObjectUtil.isEmpty(contextDef)) {
            log.error("未查询到节点[coding]配置");
            localVariables.put("statusCode", "-1");
        }
        if (ObjectUtil.isEmpty(contextDef)) {
            log.error("未查询到节点[coding]配置");
            append(context,"未查询到节点[coding]配置");
            localVariables.put("statusCode", "-1");
        }

        if (contextDef instanceof PipTask pipTask){
            Map<String, Object> codingConfigInfo = pipTask.getTaskProperties();
            String scaUploadUrl = ConstantContextHolder.getScaIp() + "/openapi/v1/vcs/detect";

            try {

                RestTemplate restTemplate = getRestTemplateWithoutSANCheck();
                // 1. 构建表单数据（文件 + 文本参数）
                HashMap<String, Object> body = new HashMap<>();
                body.put("projectName", codingConfigInfo.get("projectName"));         // 项目名称（必填）
                body.put("applicationName", codingConfigInfo.get("applicationName"));         // 应用名称（必填）
                body.put("applicationVersion", codingConfigInfo.get("applicationVersion"));           // 应用版本（必填）
                body.put("applicationDescription", codingConfigInfo.get("applicationDescription"));   // 应用描述（可选）
                Map<String,Object> vcsRepo = new HashMap<>();
                body.put("vcsRepo",vcsRepo);
                Integer type = Integer.valueOf((String) codingConfigInfo.get("type"));
                vcsRepo.put("type", type);
                if (REPO_TYPE_GIT.equals(type)) {
                    append(context,"===============SCA-GIT仓库配置=================");
                    HashMap<String,String> gitVCSAssetRepo = new HashMap<>();
                    vcsRepo.put("gitVCSAssetRepo",gitVCSAssetRepo);
                    gitVCSAssetRepo.put("url", (String) codingConfigInfo.get("url"));
                    gitVCSAssetRepo.put("username", (String) codingConfigInfo.get("username"));
                    gitVCSAssetRepo.put("password", (String) codingConfigInfo.get("password"));
                    gitVCSAssetRepo.put("branch", (String) codingConfigInfo.get("branch"));
                }

                // 2. 设置请求头
                HttpHeaders headers = createHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                headers.add("OpenApiUserToken", ConstantContextHolder.getScaToken());
                String bodyStr = JSON.toJSONString(body);
                // 3. 封装请求实体
                HttpEntity<String> requestEntity = new HttpEntity<>(bodyStr, headers);
                // 4. 发送请求并获取响应
                log.info("SCA上传接口:" + scaUploadUrl);
                append(context,"SCA上传接口:" + scaUploadUrl);
                JSONObject response = restTemplate.postForObject(scaUploadUrl, requestEntity, JSONObject.class);
                append(context,"SCA上传接口响应:" + response);

                if (response.getString("message").equals("success")) {
                    append(context,"===============SCA上传成功=================");
                    JSONObject data = response.getJSONObject("data");
                    Integer scaTaskId = data.getInteger("scaTaskId");
                    append(context,"SCA-代码仓库管理节点执行成功，SCA任务ID为：" + scaTaskId);

                    //轮询请求状态
                    pollTaskStatus(restTemplate, scaTaskId,context,codingConfigInfo,pipTask);
                } else if (response.getString("message").equals("应用已经存在")){
                    append(context,response.getString("message"));
                    Integer oldScaTaskId = (Integer) codingConfigInfo.get("scaTaskId");
                    Integer oldApplicationId = (Integer) codingConfigInfo.get("applicationId");
                    append(context,"SCA-代码仓库管理节点应用已存在，正在准备重试，SCA任务ID为：" + oldScaTaskId);
                    int restartResult  = reStartTask(restTemplate, oldApplicationId,context);
                    if (restartResult != 0) {
                        append(context,"SCA-代码仓库管理节点重试失败，请检查当前节点配置!");
                        localVariables.put("statusCode", "-1");
                    }
                    //轮询请求状态
                    pollTaskStatus(restTemplate, oldScaTaskId,context, codingConfigInfo, pipTask);
                } else {
                    //基于第三方文档表明500状态基本属于无镜像配置，对比失败，于是打印日志为无法对比镜像
                    append(context,"==================SCA校验失败=================");
                    append(context,"接口异常调用失败");
                    append(context,response.getString("message"));
                    localVariables.put("statusCode", "-1");
                }
            }catch (Exception e){
                append(context,"==================SCA-CODING节点执行失败=================");
                append(context,"SCA-CODING节点执行失败失败，请检查当前节点配置!");
                append(context,e.getMessage());
                log.error("==================SCA-CODING节点执行失败=================",e);
                throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"SCA-SBOM节点执行失败");
            }
        }

        localVariables.put("statusCode", statusCode + "");
    }


    /**
     * 创建请求头
     *
     * @return HttpHeaders
     */
    private HttpHeaders createHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.add("OpenApiUserToken", ConstantContextHolder.getScaToken());
        return headers;
    }

    /**
     * 轮询请求任务状态
     *
     * @param restTemplate
     * @param scaTaskId
     * @param codingConfigInfo
     * @param pipTask
     */
    public void pollTaskStatus(RestTemplate restTemplate, Integer scaTaskId, BaseRunContext context, Map<String, Object> codingConfigInfo, PipTask pipTask) {
        int currentPollingTimes = 0;
        while (currentPollingTimes < MAX_POLLING_TIMES) {
            try {
                HttpHeaders headers = new HttpHeaders();
                headers.add("OpenApiUserToken", ConstantContextHolder.getScaToken());
                HttpEntity<String> requestEntity = new HttpEntity<>(null, headers);
                String scaStatusUrl = ConstantContextHolder.getScaIp() + "/openapi/v1/task/" + scaTaskId;
                ResponseEntity<JSONObject> response = restTemplate.exchange(scaStatusUrl, HttpMethod.GET, requestEntity, JSONObject.class);

                if (Objects.requireNonNull(response.getBody()).getString("message").equals("success")) {
                    //"status": 5, //状态 0-未审计 1-未检测 2-排队中 3-检测中 4-检测暂停 5-检测完成 6-检测超时 7-手动停止 8-检测异常 9-已删除 10-拉取中 11-停止中 12-下载中
                    int status = response.getBody().getJSONObject("data").getInteger("status");
                    append(context,"\"状态 0-未审计 1-未检测 2-排队中 3-检测中 4-检测暂停 5-检测完成 6-检测超时 7-手动停止 8-检测异常 9-已删除 10-拉取中 11-停止中 12-下载中\"");
                    log.info("当前任务状态: " + status);
                    append(context,"当前任务状态: " + status);
                    if (status == 5) {
                        System.out.println("任务已完成，停止轮询。");
                        log.info("任务已完成，停止轮询。");
                        append(context,"任务已完成，停止状态获取。" );

                        //回调报告生成接口
                        ReportAssetTaskReq req = new ReportAssetTaskReq();
                        req.setScaTaskId(scaTaskId);
                        ReportResp reportResp = reportService.assetReportTask(req);
                        if (!ObjectUtils.isEmpty(reportResp)){
                            if (reportResp.getMessage().equals("success")){
                                append(context,"回调报告生成接口成功");
                                codingConfigInfo.put("reportId",reportResp.getId());
                                codingConfigInfo.put("reportType",req.getType());
                            }else {
                                append(context,"回调报告生成接口失败,scaTaskId = " + scaTaskId + "=====错误原因=====" + reportResp.getMessage());
                            }
                        }

                        //插入报告数据到reportInfo
                        BaseIdReq baseIdReq = new BaseIdReq();
                        baseIdReq.setTaskId(pipTask.getId());
                        baseIdReq.setScaTaskId(String.valueOf(scaTaskId));
                        reportService.createVulListByScaTaskId(baseIdReq);

                        //更新scaTaskId数据到task表
                        codingConfigInfo.put("scaTaskId",scaTaskId);
                        pipTask.setTaskProperties(codingConfigInfo);
                        pipTaskDao.updateById(pipTask);

                        break;
                    }
                } else {
                    log.error("获取任务状态失败: " + response.getBody().getString("message"));
                    append(context,"获取任务状态失败" );
                    break;
                }
            } catch (Exception e) {
                log.error("获取任务状态时发生错误: " + e.getMessage());
                append(context,"获取任务状态时发生错误" );
            }
            try {
                // 轮询间隔 5 秒
                Thread.sleep(POLLING_INTERVAL);
            } catch (InterruptedException e) {
//                Thread.currentThread().interrupt();
                toBadEnding();
                log.error("轮询被中断: " + e.getMessage());
            }
            currentPollingTimes++;
        }
        System.out.println("停止轮询");
    }

    /**
     * 重新检测接口
     *
     * @param restTemplate
     * @param applicationId
     */
    public int reStartTask(RestTemplate restTemplate, Integer applicationId,  BaseRunContext context) {
        try {
            String url = ConstantContextHolder.getScaIp() + "/openapi/v1/task/batch/detect";
            HttpHeaders headers = new HttpHeaders();
            headers.add("OpenApiUserToken", ConstantContextHolder.getScaToken());
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());
            Map<String, List<Integer>> param = new HashMap<>();
            param.put("applicationIds", Arrays.asList(applicationId));
            String s = JSON.toJSONString(param);
            HttpEntity<String> formEntry = new HttpEntity<>(s, headers);

            JSONObject res = restTemplate.postForObject(url, formEntry, JSONObject.class);
            if (res.getString("message").equals("success")) {
                log.info("重新检测成功");
                append(context,"重新检测成功");
                return 0;
            } else {
                log.error("重新检测失败");
                log.error(res.getString("message"));
                append(context,"重新检测失败:" + res.getString("message"));
                return -1;
            }
        } catch (Exception e) {
            log.error("重新检测失败");
            log.error(e.getMessage());
            append(context,"重新检测失败:" + e.getMessage());
        }
        return 0;
    }

}
