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

import cd.casic.ci.process.dto.resp.aflCrashesAnalyzer.AflCrashesAnalyzerListResp;
import cd.casic.ci.process.dto.resp.resource.ResourceDetailResp;
import cd.casic.ci.process.process.converter.AflCrashesAnalyzerConverter;
import cd.casic.ci.process.process.dao.aflCrashAnalyzer.AflCrashesAnalyzerInfoDao;
import cd.casic.ci.process.process.dao.aflCrashAnalyzer.LinuxSignalsDao;
import cd.casic.ci.process.process.dataObject.aflCrashAnalyzer.AflCrashesAnalyzerInfo;
import cd.casic.ci.process.process.dataObject.aflCrashAnalyzer.LinuxSignals;
import cd.casic.ci.process.process.service.aflAnalyzer.AflCrashesAnalyzerService;
import cd.casic.framework.commons.pojo.PageParam;
import cd.casic.framework.commons.pojo.PageResult;
import cd.casic.module.machine.dal.dataobject.MachineInfoDO;
import cd.casic.module.machine.utils.CryptogramUtil;
import com.amazonaws.util.IOUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jcraft.jsch.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *  afl crashes文件分析service类
 * */
@Service
@Slf4j
public class AflCrashesAnalyzerServiceImpl implements AflCrashesAnalyzerService {
    @Resource
    private LinuxSignalsDao signalsMapper;
    @Resource
    private AflCrashesAnalyzerInfoDao crashesInfoDao;
    @Resource
    private AflCrashesAnalyzerConverter converter;
    public PageResult<AflCrashesAnalyzerListResp> getCrashesPageList(PageParam page){
        LambdaQueryWrapper<AflCrashesAnalyzerInfo> wrapper = new LambdaQueryWrapper<>();
        PageResult<AflCrashesAnalyzerInfo> infoPage = crashesInfoDao.selectPage(page, wrapper);
        List<LinuxSignals> linuxSignals = signalsMapper.selectList();
        Map<String, LinuxSignals> signMap = linuxSignals.stream().collect(Collectors.toMap(LinuxSignals::getId, Function.identity()));
        List<AflCrashesAnalyzerInfo> list = infoPage.getList();
        List<AflCrashesAnalyzerListResp> respList = converter.converter(list);
        respList.forEach(item ->{
            LinuxSignals signal = signMap.get(item.getSignId());
            item.setBriefDesc(signal.getBriefDesc());
            item.setDetailDesc(signal.getDetailDesc());
            item.setSignalName(signal.getSignalName());
            item.setCommonScenarios(signal.getCommonScenarios());
        });
        return new PageResult<AflCrashesAnalyzerListResp>(respList,infoPage.getTotal(), infoPage.getPageNum(), infoPage.getPageSize());
    }
    @Override
    public void saveCrashesByOutputPath(String outputPath, ResourceDetailResp resource, String pipelineId, String taskId, String pipelineHistoryId) {
        log.info("afl-crash-analyzer信息入库开始执行:{},{},{},{}",outputPath,pipelineId,taskId,pipelineHistoryId);
        MachineInfoDO machineInfo = resource.getMachineInfo();
        List<AflCrashesAnalyzerInfo> dbList= new LinkedList<>();
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(machineInfo.getUsername(),machineInfo.getHostIp(), machineInfo.getSshPort());
            session.setPassword(CryptogramUtil.doDecrypt(machineInfo.getPassword()));
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();

            Channel channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp sftpChannel = (ChannelSftp) channel;

            String fullOutputPath = outputPath  + "/per-signal";
            Vector<ChannelSftp.LsEntry> files = sftpChannel.ls(fullOutputPath);
            List<LinuxSignals> linuxSignals = signalsMapper.selectList();
            Map<String, LinuxSignals> signCodeMap = linuxSignals.stream().collect(Collectors.toMap(LinuxSignals::getId, Function.identity()));
            for (ChannelSftp.LsEntry entry : files) {
                String filename = entry.getFilename();
                if (!filename.equals(".") && !filename.equals("..")) {
                    if (entry.getAttrs().isDir()) {
                        //文件夹下第一级是信号分类，下面就是crash文件
                        LinuxSignals sign = signCodeMap.get(filename);
                        if (sign==null) {
                            log.error("未识别信号类型");
                            continue;
                        }
                        String crashesTypes = fullOutputPath + "/" + sign.getId();
                        // 查找crashes 的分类下的crashes
                        Vector<ChannelSftp.LsEntry> crashes = sftpChannel.ls(crashesTypes);
                        for (ChannelSftp.LsEntry crashe : crashes) {
                            String crashFilename = crashe.getFilename();
                            if (!crashe.getAttrs().isDir()) {
                                System.out.println(crashesTypes + "/" + crashFilename);
                                InputStream crashesInputStream = ((ChannelSftp) channel).get(crashesTypes + "/"+ crashFilename);
                                AflCrashesAnalyzerInfo crashesInfo = new AflCrashesAnalyzerInfo();
                                crashesInfo.setCrashName(crashe.getFilename());
                                crashesInfo.setSignId(sign.getId());
                                crashesInfo.setPipelineHistoryId(pipelineHistoryId);
                                crashesInfo.setPipelineId(pipelineId);
                                crashesInfo.setTaskId(taskId);
                                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                                IOUtils.copy(crashesInputStream,bos);
                                crashesInfo.setCrashesContent(bos.toByteArray());
                                dbList.add(crashesInfo);
                            }
                        }
                    }
                }
            }
            if (!CollectionUtils.isEmpty(dbList)) {
                crashesInfoDao.insertBatch(dbList);
            }
            sftpChannel.disconnect();
            session.disconnect();
        } catch (JSchException | SftpException e) {
            e.printStackTrace();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
