package cd.casic.ci.process.process.service.aflManager.impl;

import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
import cd.casic.ci.process.dto.resp.aflManager.AflCrashesInfoResp;
import cd.casic.ci.process.dto.resp.aflManager.AflCrashesResp;
import cd.casic.ci.process.dto.resp.resource.ResourceDetailResp;
import cd.casic.ci.process.engine.constant.PipelineVariableConstant;
import cd.casic.ci.process.engine.manager.RunContextManager;
import cd.casic.ci.process.engine.runContext.BaseRunContext;
import cd.casic.ci.process.process.converter.AflManagerConverter;
import cd.casic.ci.process.process.dao.aflManager.AflCrashesInfoDao;
import cd.casic.ci.process.process.dataObject.aflManager.AflCrashesInfo;
import cd.casic.ci.process.process.dataObject.aflManager.AflInfo;
import cd.casic.ci.process.process.service.aflManager.AflCrashesInfoService;
import cd.casic.ci.process.process.service.resource.ResourceManagerService;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import cd.casic.module.machine.dal.dataobject.MachineInfoDO;
import cd.casic.module.machine.utils.CryptogramUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import de.danielbechler.util.Collections;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author HopeLi
 * @version v1.0
 * @ClassName TargetVersionServiceImpl
 * @Date: 2025/5/17 15:20
 * @Description:
 */
@Service
@Slf4j
public class AflCrashesInfoServiceImpl extends ServiceImpl<AflCrashesInfoDao, AflCrashesInfo> implements AflCrashesInfoService {

    private static final String remoteFilePath = "/home/casic/706/yunqi/";
    private static final String crashesFilePath = "/ai_afl/default/crashes/";

    @Resource
    private RunContextManager runContextManager;
    @Resource
    private ResourceManagerService resourceManagerService;



    @Override
    public String saveAflCrashesInfo(AflManagerReq req) {
        List<AflCrashesInfo> aflCrashesInfos = new ArrayList<>(0);

        if (StringUtils.isEmpty(req.getPipelineId()) || StringUtils.isEmpty(req.getTaskId())){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"pipelineId或者taskId不能为空");
        }
        String uuid = req.getGroupIdentifier();

        BaseRunContext runContext = runContextManager.getContext(req.getPipelineId());
        if (ObjectUtils.isEmpty(runContext)){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到运行上下文");
        }
        String resourceId = runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY) instanceof String ? ((String) runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY)) : null;
//        String resourceId = "2";
        if (StringUtils.isEmpty(resourceId)){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到资源");
        }

        ResourceDetailResp resourceById = resourceManagerService.findResourceDetailById(resourceId);
        if (resourceById == null || resourceById.getMachineInfo() == null || resourceById.getMachineInfo().getAuthenticationType()!=1) {
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"资源信息错误");
        }

        MachineInfoDO resourceMachine = resourceById.getMachineInfo();
        String password = CryptogramUtil.doDecrypt(resourceMachine.getPassword());
        resourceMachine.setPassword(password);

        try {
            // 步骤1：使用自定义sql获取崩溃文件的创建时间
            //查询创建时间
            List<String> resultList = SftpUploadUtil.findCreateTimeByCommand(
                    resourceMachine.getHostIp(),
                    resourceMachine.getSshPort(),
                    resourceMachine.getUsername(),
                    resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);

            if (!CollectionUtils.isEmpty(resultList)){
                //解析文件名和创建时间
                for (String o : resultList) {
                    String[] lineParts = o.split("\\s+创建时间:\\s+");
                    if (lineParts.length < 2) {
                        System.err.println("无效格式: " + o);
                        break;
                    }
                    AflCrashesInfo aflCrashesInfo = new AflCrashesInfo();

                    String fileName = lineParts[0].substring(2).trim(); // 文件信息部分
                    String createTime = lineParts[1].trim();   // 创建时间
                    aflCrashesInfo.setCrashesName(fileName);
                    aflCrashesInfo.setCrashesCreateTime(createTime);

                    aflCrashesInfos.add(aflCrashesInfo);
                }
            }



            // 步驟2.查询文件大小
            List<String> fileResultList = SftpUploadUtil.findFileByteByCommand(
                    resourceMachine.getHostIp(),
                    resourceMachine.getSshPort(),
                    resourceMachine.getUsername(),
                    resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);

            if (!CollectionUtils.isEmpty(fileResultList) && !CollectionUtils.isEmpty(aflCrashesInfos)){
                //解析文件名和文件大小
                for (String o : fileResultList) {
                    // 按空白字符分割
                    String[] parts = o.trim().split("\\s+");

                    String fileName = parts[0]; // 文件名称
                    Integer fileLength = Integer.parseInt(parts[1]);   // 文件大小
                    for (AflCrashesInfo aflCrashesInfo : aflCrashesInfos) {
                        if (aflCrashesInfo.getCrashesName().equals(fileName)) {
                            aflCrashesInfo.setCrashesLength(fileLength);

                            aflCrashesInfo.setPipelineId(req.getPipelineId());
                            if (!StringUtils.isEmpty(req.getTaskId())){
                                aflCrashesInfo.setTaskId(req.getTaskId());
                            }
                            aflCrashesInfo.setGroupIdentifier(uuid);
                        }
                    }
                }
            }



            // 步骤3：列出源目录下的所有文件
            List<String> files = SftpUploadUtil.listFilesInRemoteDirectory(
                    resourceMachine.getHostIp(), resourceMachine.getSshPort(), resourceMachine.getUsername(), resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);

            if (!CollectionUtils.isEmpty(files)) {
                // 步骤4：批量下载文件流与文件绑定
                Map<String,byte[]> copiedFiles = SftpUploadUtil.copyRemoteFilesToByteArray(
                        resourceMachine.getHostIp(), resourceMachine.getSshPort(), resourceMachine.getUsername(), resourceMachine.getPassword(), null, files);
                System.out.println("共下载 " + copiedFiles.size() + " 个文件");

                //绑定对应的文件流
                if (!ObjectUtils.isEmpty(copiedFiles)){
                    for (Map.Entry<String, byte[]> entry : copiedFiles.entrySet()) {
                        if (!CollectionUtils.isEmpty(aflCrashesInfos)){
                            for (AflCrashesInfo aflCrashesInfo : aflCrashesInfos) {
                                if (aflCrashesInfo.getCrashesName().equals(entry.getKey())) {
                                    aflCrashesInfo.setFileContent(entry.getValue());
                                }
                                aflCrashesInfo.setPipelineId(req.getPipelineId());
                                if (!StringUtils.isEmpty(req.getTaskId())){
                                    aflCrashesInfo.setTaskId(req.getTaskId());
                                }
                                if (!StringUtils.isEmpty(req.getPipelineHistoryId())){
                                    aflCrashesInfo.setPipelineHistoryId(req.getPipelineHistoryId());
                                }
                                aflCrashesInfo.setGroupIdentifier(uuid);
                            }
                        }
                    }

                    //新增数据到crashes表中
                    baseMapper.insertBatch(aflCrashesInfos);
                }
                return uuid;
            }else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"crashes入库失败");
        }
    }

    @Override
    public void updateHistoryPipelineIdByAflCrashesInfo(AflManagerReq req) {
        if (!StringUtils.isEmpty(req.getGroupIdentifier())){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"groupIdentifier不能为空");
        }
        QueryWrapper<AflCrashesInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("group_identifier", req.getGroupIdentifier());
        List<AflCrashesInfo> aflCrashesInfos = baseMapper.selectList(wrapper);
        if (!Collections.isEmpty(aflCrashesInfos)){
            aflCrashesInfos.forEach(o->{
                o.setPipelineHistoryId(req.getPipelineHistoryId());
            });
        }
        baseMapper.updateBatch(aflCrashesInfos);
    }

    @Override
    public List<AflCrashesInfoResp> findAflCrashesInfoListByHistory(AflManagerReq req) {
        QueryWrapper<AflCrashesInfo> wrapper = new QueryWrapper<>();
        if (!ObjectUtils.isEmpty(req.getId())){
            wrapper.eq("id",req.getId());
        }
        if (!ObjectUtils.isEmpty(req.getPipelineId())){
            wrapper.eq("pipeline_id",req.getPipelineId());
        }
        if (!ObjectUtils.isEmpty(req.getPipelineHistoryId())){
            wrapper.eq("pipeline_history_id",req.getPipelineHistoryId());
        }
        if (!ObjectUtils.isEmpty(req.getTaskId())){
            wrapper.eq("task_id",req.getTaskId());
        }
        if (!ObjectUtils.isEmpty(req.getGroupIdentifier())){
            wrapper.eq("group_identifier",req.getGroupIdentifier());
        }

        List<AflCrashesInfo> aflCrashesInfos = baseMapper.selectList(wrapper);
        if (ObjectUtils.isEmpty(aflCrashesInfos)){
            return new ArrayList<>(0);
        }

        return AflManagerConverter.INSTANCE.toAflCrashesInfoRespList(aflCrashesInfos);
    }

    @Override
    public AflCrashesResp findCrashesCount(AflManagerReq req) {
        AflCrashesResp aflCrashesResp = new AflCrashesResp();

        if (StringUtils.isEmpty(req.getPipelineId()) || StringUtils.isEmpty(req.getTaskId())){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"pipelineId或者taskId不能为空");
        }

        BaseRunContext runContext = runContextManager.getContext(req.getPipelineId());
        if (ObjectUtils.isEmpty(runContext)){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到运行上下文");
        }
        String resourceId = runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY) instanceof String ? ((String) runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY)) : null;
        if (StringUtils.isEmpty(resourceId)){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到资源");
        }

        ResourceDetailResp resourceById = resourceManagerService.findResourceDetailById(resourceId);
        if (resourceById == null || resourceById.getMachineInfo() == null || resourceById.getMachineInfo().getAuthenticationType()!=1) {
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"资源信息错误");
        }

        MachineInfoDO resourceMachine = resourceById.getMachineInfo();
        String password = CryptogramUtil.doDecrypt(resourceMachine.getPassword());
        resourceMachine.setPassword(password);

        try {
            // 查询所有崩溃数
            List<String> files = SftpUploadUtil.listFilesInRemoteDirectory(
                    resourceMachine.getHostIp(), resourceMachine.getSshPort(), resourceMachine.getUsername(), CryptogramUtil.doDecrypt(resourceMachine.getPassword()), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);

            if (!CollectionUtils.isEmpty(files)){
                aflCrashesResp.setImperfectionCount(files.size());
            }

            //查询已验证和未验证缺陷数
            AflInfo aflInfo = SftpUploadUtil.downloadFileSftpForInputStreamAndSetAflInfo(resourceMachine.getHostIp(), resourceMachine.getSshPort(), resourceMachine.getUsername(), resourceMachine.getPassword(),null, remoteFilePath + "PIP_" + req.getPipelineId() + "/ai_afl/default/fuzzer_stats");
            if (!ObjectUtils.isEmpty(aflInfo)){
                aflCrashesResp.setVerifiedCount(Integer.parseInt(aflInfo.getSavedCrashes()));
                aflCrashesResp.setUnverifiedCount(Integer.parseInt(aflInfo.getPendingFavs()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return aflCrashesResp;
    }

    @Override
    public List<AflCrashesInfoResp> findCrashesNow(AflManagerReq req) {
        List<AflCrashesInfoResp> respList = new ArrayList<>(0);
        if (StringUtils.isEmpty(req.getPipelineId()) || StringUtils.isEmpty(req.getTaskId())){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"pipelineId或者taskId不能为空");
        }

        BaseRunContext runContext = runContextManager.getContext(req.getPipelineId());
        if (ObjectUtils.isEmpty(runContext)){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到运行上下文");
        }
        String resourceId = runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY) instanceof String ? ((String) runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY)) : null;
//        String resourceId = "2";
        if (StringUtils.isEmpty(resourceId)){
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到资源");
        }

        ResourceDetailResp resourceById = resourceManagerService.findResourceDetailById(resourceId);
        if (resourceById == null || resourceById.getMachineInfo() == null || resourceById.getMachineInfo().getAuthenticationType()!=1) {
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"资源信息错误");
        }

        MachineInfoDO resourceMachine = resourceById.getMachineInfo();
        String password = CryptogramUtil.doDecrypt(resourceMachine.getPassword());
        resourceMachine.setPassword(password);

        try {
            // 步骤1：使用自定义sql获取崩溃文件的创建时间
            //查询创建时间
            List<String> resultList = SftpUploadUtil.findCreateTimeByCommand(
                    resourceMachine.getHostIp(),
                    resourceMachine.getSshPort(),
                    resourceMachine.getUsername(),
                    resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);

            if (CollectionUtils.isEmpty(resultList)){
                return respList;
            }

            //解析文件名和创建时间
            for (String o : resultList) {
                String[] lineParts = o.split("\\s+创建时间:\\s+");
                if (lineParts.length < 2) {
                    System.err.println("无效格式: " + o);
                    break;
                }
                AflCrashesInfoResp aflCrashesInfoResp = new AflCrashesInfoResp();

                String fileName = lineParts[0].substring(2).trim(); // 文件信息部分
                String createTime = lineParts[1].trim();   // 创建时间
                aflCrashesInfoResp.setCrashesName(fileName);
                aflCrashesInfoResp.setCrashesCreateTime(createTime);
                aflCrashesInfoResp.setFilePath(remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath + fileName);

                respList.add(aflCrashesInfoResp);
            }

            // 步驟2.查询文件大小
            List<String> fileResultList = SftpUploadUtil.findFileByteByCommand(
                    resourceMachine.getHostIp(),
                    resourceMachine.getSshPort(),
                    resourceMachine.getUsername(),
                    resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);

            if (CollectionUtils.isEmpty(fileResultList)){
                return null;
            }

            //解析文件名和文件大小
            for (String o : fileResultList) {
                // 按空白字符分割
                String[] parts = o.trim().split("\\s+");

                String fileName = parts[0]; // 文件名称
                Integer fileLength = Integer.parseInt(parts[1]);   // 文件大小
                if (!CollectionUtils.isEmpty(respList)){
                    for (AflCrashesInfoResp aflCrashesInfoResp : respList) {
                        if (aflCrashesInfoResp.getCrashesName().equals(fileName)) {
                            aflCrashesInfoResp.setCrashesLength(fileLength);
                        }
                    }
                }
            }
            return respList;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"crashes入库失败");
        }
    }
}
