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

import cd.casic.ci.process.common.WorkAtom;
import cd.casic.ci.process.dto.req.resource.ResourceQueryReq;
import cd.casic.ci.process.dto.resp.resource.ResourceFindResp;
import cd.casic.ci.process.dto.resp.taskResource.TaskResourceFindResp;
import cd.casic.ci.process.engine.constant.AFLSlotCompileConstant;
import cd.casic.ci.process.engine.constant.DIYImageExecuteCommandConstant;
import cd.casic.ci.process.engine.constant.PipelineGlobalVariableConstant;
import cd.casic.ci.process.engine.runContext.TaskRunContext;
import cd.casic.ci.process.engine.worker.base.DockerWorker;
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
import cd.casic.ci.process.process.dataObject.pipeline.PipPipeline;
import cd.casic.ci.process.process.dataObject.resource.PipResourceMachine;
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.resource.ResourceManagerService;
import cd.casic.ci.process.process.service.target.TargetVersionService;
import cd.casic.ci.process.util.CryptogramUtil;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import cd.casic.module.execute.docker.dataobject.dto.DockerEndpointDo;
import com.alibaba.fastjson.JSON;
import com.jcraft.jsch.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;

@WorkAtom(taskType = "AFL_SLOT_COMPILE")
@Slf4j
public class AFLSlotCompileWorker extends DockerWorker {
    @Resource
    private TargetVersionService targetVersionService;
    @Override
    public void execute(TaskRunContext context) {
        PipTask task = context.getContextDef() instanceof PipTask ? ((PipTask) context.getContextDef()) : null;
        // 每一个流水线不同目录互不干扰
        String workDir = PipelineGlobalVariableConstant.AFL_DOCKER_WORK_DIR_PREFIX + task.getPipelineId();
        Map<String, Object> globalVariables = context.getGlobalVariables();
        if (!globalVariables.containsKey(workDir)) {
            globalVariables.put(PipelineGlobalVariableConstant.AFL_DOCKER_WORK_DIR_KEY,workDir);
        }
        Map<String, Object> taskProperties = task.getTaskProperties();
        String managerId = taskProperties.get(AFLSlotCompileConstant.MANAGER_ID) instanceof String ? ((String) taskProperties.get(AFLSlotCompileConstant.MANAGER_ID)) : null;
        // ssh 上传目标文件
        ResourceFindResp resourceById = resourceManagerService.findResourceById(managerId);
        String machineId = resourceById.getMachineId();
        String dockerId = resourceById.getDockerId();
        String imageName = taskProperties.get(AFLSlotCompileConstant.IMAGE_NAME) instanceof String ? ((String) taskProperties.get(AFLSlotCompileConstant.IMAGE_NAME)) : null;
        if (StringUtils.isEmpty(machineId)||StringUtils.isEmpty(dockerId)) {
            append(context,"该资源不支持docker或者ssh");
        }
        ResourceQueryReq req = new ResourceQueryReq();
        req.setId(machineId);
        req.setType("machine");
        TaskResourceFindResp machineQuery = resourceManagerService.findResourceListByType(req);
        req.setId(dockerId);
        req.setType("docker");
        TaskResourceFindResp dockerQuery = resourceManagerService.findResourceListByType(req);
        PipResourceMachine machineInfo = machineQuery.getResourceMachineList().get(0);
        DockerEndpointDo dockerInfo = dockerQuery.getDockerEndpointList().get(0);
        PipPipeline pipeline = (PipPipeline) getContextManager().getContext(task.getPipelineId()).getContextDef();
        // 获取目标文件
        TargetVersion targetVersion = targetVersionService.getById(pipeline.getTargetVersionId());
        String fileName = targetVersion.getFileName();
        append(context,"读取到当前文件名称:"+fileName);
        String filePath = targetVersion.getFilePath();
        File file = new File(filePath);
        if (!file.exists() || !file.canRead()) {
            log.error("目标文件不存在或不可读");
            append(context,"目标文件不存在或不可读");
            toBadEnding();
        }
        // 工作目录下的相对路径,/test 作为根路径
        // 卷挂载目录+流水线目录 + task编译目录
        String realPath = PipelineGlobalVariableConstant.AFL_VOLUME_WORK_DIR_PREFIX+File.separator+ workDir;
        // 上传目标文件 到指定资源服务器
        try {
            append(context,"AFL编译，上传文件路径:"+realPath);
            SftpUploadUtil.uploadFileViaSftp(
                    machineInfo.getMachineHost()
                    ,Integer.valueOf(machineInfo.getSshPort()),machineInfo.getUsername(), CryptogramUtil.doDecrypt(machineInfo.getPassword()),"",filePath,realPath,file.getName());
        } catch (SftpUploadUtil.SftpUploadException e) {
            append(context,"上传文件失败，请确认资源信息是否有误:"+JSON.toJSONString(machineInfo));
            log.error("上传文件报错",e);
            toBadEnding();
        }
        // 执行预设命令 ,进入目录
        String allCommand = "docker run -v "+ PipelineGlobalVariableConstant.AFL_VOLUME_WORK_DIR_PREFIX +":"+PipelineGlobalVariableConstant.AFL_DOCKER_BASE_DIR+"  -it "+imageName+" bash\n" +
                "cd "+PipelineGlobalVariableConstant.AFL_DOCKER_BASE_DIR+"\n"+// 进入到容器中的卷挂载目录
        "mkdir -p "+workDir+"\n"+
        "chmod -R o+rwx "+ workDir +"\n"+
        "cd "+workDir+"\n"+handleZipFile(fileName);
        Object commandScriptObj = taskProperties.get(AFLSlotCompileConstant.COMMAND_SCRIPT);
        String commandScript = commandScriptObj instanceof String ? ((String) commandScriptObj) : null;
        allCommand += commandScript+"\n";
        log.info("AFL插装编译容器内执行的命令：{}",allCommand);
        append(context,"AFL插装编译容器内执行的命令："+allCommand);
        dockerRun(allCommand,dockerInfo,context);
        // 更新全局变量
        // /test目录下当前流水线工作目录
        globalVariables.put(PipelineGlobalVariableConstant.AFL_DOCKER_WORK_DIR_KEY,workDir);
        putInCompileResult(context,realPath+File.separator+getSourceName(fileName),machineInfo);
    }
    private String handleZipFile(String fileName){
        if (StringUtils.isEmpty(fileName)) {
            return "";
        }
        int i = fileName.lastIndexOf(".");
        if (i==-1||i>=fileName.length()-1) {
            return "";
        }
        String prefix = fileName.substring(0, i);
        String suffix = fileName.substring(i + 1);
        if ("zip".equals(suffix)) {
            String cmd ="unzip -o "+fileName+"\n" +
                    "chmod -R o+rwx "+prefix+"/\n"+
                    "cd "+prefix+"\n";
            return cmd;
        }
        if ("tar".equals(suffix)) {
            String cmd ="tar -zxvf "+fileName+"\n" +
                    "chmod -R o+rwx "+prefix+"/\n"+
                    "cd "+prefix+"\n";
            return cmd;
        }
        return "";
    }

    private void putInCompileResult(TaskRunContext context,String path, PipResourceMachine machineInfo){
        String host = machineInfo.getMachineHost();
        String user = machineInfo.getUsername();
        String password = CryptogramUtil.doDecrypt(machineInfo.getPassword());
        append(context,"AFL编译完毕");
        List<String> fileNameList = null;
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(user, host, 22);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();
            ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
            sftpChannel.connect();
            Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(path);
            fileNameList = new ArrayList<>(files.size());
            for (ChannelSftp.LsEntry file : files) {
                if (!file.getAttrs().isDir()&&!file.getFilename().contains(".")) {
                    fileNameList.add(file.getFilename());
                }
            }
            sftpChannel.disconnect();
            session.disconnect();
        } catch (JSchException | SftpException e) {
            e.printStackTrace();
        }
        if (!CollectionUtils.isEmpty(fileNameList)&&context!=null) {
            Map<String, Object> taskProperties = context.getGlobalVariables();
            // 放入全局上下文中
            append(context,"AFL编译完毕:"+JSON.toJSONString(fileNameList));
            taskProperties.put(AFLSlotCompileConstant.COMPILE_RESULT,fileNameList);
        }
    }
    public String getSourceName(String fileName){
        int dotIndex = fileName.lastIndexOf(".");
        if (dotIndex!=-1) {
            return fileName.substring(0, dotIndex);
        }
        return "";
    }
}
