package com.ruoyi.platform.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.annotations.CheckDuplicate;
import com.ruoyi.platform.domain.*;
import com.ruoyi.platform.domain.dependencydomain.TrainTaskDepency;
import com.ruoyi.platform.mapper.*;
import com.ruoyi.platform.service.*;
import com.ruoyi.platform.utils.*;
import com.ruoyi.platform.vo.*;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.system.api.model.LoginUser;
import io.minio.messages.Item;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.InputStreamResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.ruoyi.common.security.utils.SecurityUtils.decrypt;

/**
 * (Models)表服务实现类
 *
 * @author Xidaray
 * @since 2023-11-28 16:26:07
 */
@Service("modelsService")
public class ModelsServiceImpl implements ModelsService {

    private static final Logger logger = LoggerFactory.getLogger(ModelsServiceImpl.class);

    @Resource
    private ModelsDao modelsDao;
    @Resource
    private ModelsVersionDao modelsVersionDao;
    @Resource
    private RayDao rayDao;
    @Resource
    private ModelsVersionService modelsVersionService;
    @Resource
    private ActiveLearnDao activeLearnDao;
    @Resource
    private DevEnvironmentDao devEnvironmentDao;
    @Resource
    private ServiceDao serviceDao;
    @Resource
    private ModelDependency1Dao modelDependency1Dao;

    @Resource
    private MinioService minioService;

    @Resource
    private GitService gitService;

    @Resource
    private AssetIconService assetIconService;

    @Resource
    @Lazy
    private AimService aimsService;

    @Resource
    private NewHttpUtils httpUtils;
    @Resource
    private DVCUtils dvcUtils;

    @Resource
    private AssetWorkflowDao assetWorkflowDao;

    // 固定存储桶名
    @Value("${minio.dataReleaseBucketName}")
    private String bucketName;
    @Resource
    private MinioUtil minioUtil;

    @Value("${spring.redis.host}")
    private String redisHost;
    @Value("${spring.redis.port}")
    private Integer redisPort;
    @Value("${git.endpoint}")
    String gitendpoint;
    @Value("${git.cloneEndpoint}")
    String gitCloneEndpoint;
    @Value("${git.cloneEndpointIp}")
    String gitCloneEndpointIp;
    @Value("${git.projectCategoryId}")
    Integer projectCategoryId;
    @Value("${git.localPath}")
    String localPath;
    @Value("${minio.accessKey}")
    String accessKeyId;
    @Value("${minio.secretKey}")
    String secretAccessKey;
    @Value("${minio.endpoint}")
    String endpoint;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public Models queryById(Integer id) {
        Models models = this.modelsDao.queryById(id);
        String modelType = models.getModelType();
        String modelTag = models.getModelTag();
        //去资产管理表中查询对应的图标名,注意判空逻辑，只有当dataType和dataTag不为空时，才进行查询
        if (modelType != null && !modelType.isEmpty()) {
            AssetIcon modelTypeAssetIcon = assetIconService.queryById(Integer.valueOf(modelType));
            if (modelTypeAssetIcon != null) {
                models.setModelTypeName(modelTypeAssetIcon.getName());
            }
        }
        if (modelTag != null && !modelTag.isEmpty()) {
            AssetIcon modelTagAssetIcon = assetIconService.queryById(Integer.valueOf(modelTag));
            if (modelTagAssetIcon != null) {
                models.setModelTagName(modelTagAssetIcon.getName());
            }
        }
        return models;
    }

    /**
     * 分页查询
     *
     * @param models      筛选条件
     * @param pageRequest 分页对象
     * @return 查询结果
     */
    @Override
    public Page<Models> queryByPage(Models models, PageRequest pageRequest) {
        long total = this.modelsDao.count(models);
        return new PageImpl<>(this.modelsDao.queryAllByLimit(models, pageRequest), pageRequest, total);
    }

    /**
     * 新增数据
     *
     * @param models 实例对象
     * @return 实例对象
     */
    @Override
    public Models insert(Models models) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        checkDeclaredName(models);
        models.setCreateBy(loginUser.getUsername());
        models.setUpdateBy(loginUser.getUsername());
        models.setUpdateTime(new Date());
        models.setCreateTime(new Date());
        models.setState(1);
        this.modelsDao.insert(models);
        return models;
    }

    /**
     * 修改数据
     *
     * @param models 实例对象
     * @return 实例对象
     */
    @Override
    public Models update(Models models) {
        int currentState = models.getState();
        if (currentState == 0) {
            throw new RuntimeException("模型已被删除，无法更新");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        models.setUpdateBy(loginUser.getUsername());
        models.setUpdateTime(new Date());
        this.modelsDao.update(models);
        return this.queryById(models.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Integer id) {
        return this.modelsDao.deleteById(id) > 0;
    }

    @Override
    public String removeById(Integer id) throws Exception {
        Models models = this.modelsDao.queryById(id);
        if (models == null) {
            throw new Exception("模型不存在");
        }

        //判断权限，只有admin和创建者本身可以删除该模型
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String username = loginUser.getUsername();


        String createdBy = models.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) {
            throw new Exception("无权限删除该模型");
        }
        //判断是否有版本文件
        if (!modelsVersionService.queryByModelsId(id).isEmpty()) {
            throw new Exception("请先删除该模型下的版本文件");
        }
        models.setState(0);
        return this.modelsDao.update(models) > 0 ? "删除成功" : "删除失败";
    }

    /**
     * 下载模型
     *
     * @param id models_version表的主键
     * @return 文件内容
     */

    @Override
    public ResponseEntity<InputStreamResource> downloadModels(Integer id) throws Exception {
        ModelsVersion modelsVersion = this.modelsVersionDao.queryById(id);
        if (modelsVersion == null) {
            throw new Exception("未找到该模型下版本记录");

        }
        // 从数据库中获取存储路径（即MinIO中的对象名称）
        String objectName = modelsVersion.getUrl();
        if (objectName == null || objectName.isEmpty()) {
            throw new Exception("未找到该版本模型文件");
        }

        try {
            // 使用ByteArrayOutputStream来捕获下载的数据
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            minioUtil.downloadObject(bucketName, objectName, outputStream);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            InputStreamResource resource = new InputStreamResource(inputStream);

            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(extractFileName(objectName), "UTF-8") + "\"")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);


        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("下载模型文件错误");
        }
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadDatasetlocal(String filePath) throws Exception {
        File file = new File(filePath);

        if (!file.exists()) {
            throw new FileNotFoundException("File not found: " + filePath);
        }

        InputStreamResource resource = new InputStreamResource(new FileInputStream(file));

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName());
        headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length()));
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);

        return ResponseEntity.ok()
                .headers(headers)
                .contentLength(file.length())
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    }


    /**
     * 上传模型文件
     *
     * @param files 文件
     * @param uuid  唯一标识
     * @return 是否成功
     */
    @Override
    public List<Map<String, String>> uploadModels(MultipartFile[] files, String uuid) throws Exception {
        List<Map<String, String>> results = new ArrayList<>();
        for (MultipartFile file : files) {
            // 构建objectName
            String username = SecurityUtils.getLoginUser().getUsername();
            String fileName = file.getOriginalFilename();
            String objectName = "models/" + username + "/" + uuid + "/" + fileName;
            results.add(minioService.uploadFile(bucketName, objectName, file));
        }
        return results;
    }


    @Override
    public Map uploadModelsPipeline(ModelsVersion modelsVersion) throws Exception {
        String username = SecurityUtils.getLoginUser().getUsername();
        // 检查是否存在
        Models models = this.queryById(modelsVersion.getModelsId());

        if (models == null) {
            throw new Exception("模型不存在，请检查模型id");
        }

        ModelsVersion version = modelsVersionService.queryByModelsVersion(modelsVersion);

        String url = "";
        if (version == null) {
            //插表,因为这里是一次直接插表所以这里定掉date，然后用DAO插入
            Date date = new Date();
            //String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(date);
            url = "models/" + username + "/" + models.getName() + "-" + "/" + modelsVersion.getVersion() + "/" + modelsVersion.getFileName();
            modelsVersion.setUrl(url);
            modelsVersion.setCreateBy(username);
            modelsVersion.setUpdateBy(username);
            modelsVersion.setCreateTime(date);
            modelsVersion.setUpdateTime(date);
            modelsVersion.setState(1);
            modelsVersionDao.insert(modelsVersion);
        } else {
            //改表
            BeansUtils.copyPropertiesIgnoreNull(modelsVersion, version);
            Date createTime = version.getCreateTime();
            // String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(createTime);
            url = "models/" + username + "/" + models.getName() + "-" + "/" + modelsVersion.getVersion() + "/" + modelsVersion.getFileName();
            version.setUrl(url);
            modelsVersionService.update(version);
        }
        Map<String, String> result = new HashMap<String, String>();
        result.put("url", url);
        return result;
    }

    /**
     * 下载所有模型文件，用压缩包的方式返回
     *
     * @param modelsId 模型ID
     * @param version  模型版本号
     * @return 文件内容
     */
    @Override
    public ResponseEntity<InputStreamResource> downloadAllModelFiles(Integer modelsId, String version) throws Exception {
        // 根据模型id查模型名
        Models model = this.modelsDao.queryById(modelsId);
        String modelName = model.getName();
        // 查询特定模型和版本对应的所有文件
        List<ModelsVersion> modelsVersionList = this.modelsVersionDao.queryAllByModelsVersion(modelsId, version);
        if (modelsVersionList == null || modelsVersionList.isEmpty()) {
            throw new Exception("模型文件记录不存在，请检查模型id");
        }

        // 创建ZIP文件，准备打包下载
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(baos)) {
            //遍历每一个version数据项
            for (ModelsVersion modelsVersion : modelsVersionList) {
                String objectName = modelsVersion.getUrl();
                if (objectName != null && !objectName.isEmpty()) {
                    // 使用ByteArrayOutputStream来捕获MinIO中的文件
                    ByteArrayOutputStream fileOutputStream = new ByteArrayOutputStream();
                    minioUtil.downloadObject(bucketName, objectName, fileOutputStream);
                    // 添加到ZIP文件
                    zos.putNextEntry(new ZipEntry(extractFileName(objectName)));
                    fileOutputStream.writeTo(zos);
                    zos.closeEntry();
                }
            }

            // 转换为输入流
            ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
            InputStreamResource resource = new InputStreamResource(inputStream);

            // 设置响应
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + modelName + "_" + version + ".zip\"")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("模型文件打包下载失败");
        }

    }

    @Override
    public List<String> getModelVersions(Integer modelId) throws Exception {
        // 获取所有相同模型ID的记录
        List<ModelsVersion> versions = modelsVersionDao.queryByModelsId(modelId);

        if (versions.isEmpty()) {
            return new ArrayList<>(); // 如果没有找到任何版本记录，返回空列表
        }

        // 使用Stream API提取version字段，并去重
        return versions.stream()
                .map(ModelsVersion::getVersion) // 提取每个ModelVersion对象的version属性
                .filter(version -> version != null && !version.isEmpty()) //忽略null或空字符串的version
                .distinct() // 去重
                .collect(Collectors.toList()); // 收集到List中
    }


    @Override
    @Transactional
    public String insertModelAndVersion(ModelsVo modelsVo) throws Exception {
        List<VersionVo> modelsVersionVos = modelsVo.getModelVersionVos();

        if (modelsVersionVos == null || modelsVersionVos.isEmpty()) {
            throw new Exception("模型版本信息错误");
        }
        Models models = new Models();
        models.setName(modelsVo.getName());
        models.setDescription(modelsVo.getDescription());
        models.setModelType(modelsVo.getModelType());
        models.setModelTag(modelsVo.getModelTag());
        Models modelsInsert = this.insert(models);
        if (modelsInsert == null) {
            throw new Exception("新增模型失败");
        }
        //遍历版本信息列表，把文件信息插入数据库
        for (VersionVo modelsVersionVo : modelsVersionVos) {
            ModelsVersion modelsVersion = new ModelsVersion();
            modelsVersion.setModelsId(modelsInsert.getId());
            modelsVersion.setVersion(modelsVo.getVersion());
            modelsVersion.setUrl(modelsVersionVo.getUrl());
            modelsVersion.setFileName(modelsVersionVo.getFileName());
            modelsVersion.setFileSize(modelsVersionVo.getFileSize());
            ModelsVersion modelsVersionInsert = this.modelsVersionService.insert(modelsVersion);
            if (modelsVersionInsert == null) {
                throw new Exception("新增模型版本失败");
            }
        }

        return "新增模型成功";

    }


    /**
     * 根据模型id和版本读取文件内容
     *
     * @param modelsId 模型ID
     * @param version  模型版本号
     * @return 文件内容
     */
    @Override
    public String readFileContent(Integer modelsId, String version) throws Exception {
        // 根据模型id查模型名
        Models model = this.modelsDao.queryById(modelsId);
        if (model == null) {
            throw new Exception("模型不存在");
        }
        String modelName = model.getName();
        String objectName = "";
        // 查询特定模型和版本对应的所有文件
        List<ModelsVersion> modelsVersionList = this.modelsVersionDao.queryAllByModelsVersion(modelsId, version);
        if (modelsVersionList == null || modelsVersionList.isEmpty()) {
            throw new Exception("对应模型版本不存在");
        }
        //遍历文件列表
        for (ModelsVersion modelsVersion : modelsVersionList) {
            String fileName = modelsVersion.getFileName();
            if ("readme.md".equalsIgnoreCase(fileName)) {
                //如果存在readme文件，读取minio中的url
                objectName = modelsVersion.getUrl();
                // 读取MinIO中的对象为字符串
                try {
                    return minioUtil.readObjectAsString(bucketName, objectName);
                } catch (Exception e) {
                    throw new Exception("读取README.md文件失败", e);
                }
            }
        }

        // 如果没有找到README.md文件，返回null或者抛出一个异常
        throw new Exception("未找到README.md文件");


    }

    @Override
    public void checkDeclaredName(Models insert) throws Exception {
        Models existingModel = modelsDao.findByName(insert.getName());
        if (existingModel != null) {
            // Check if the found models is not the same as the one being inserted
            // This is important if you are using this method for both insert and update operations
            // You may need an identifier check here, e.g., if 'insert' has an ID and it's the same as 'existingDataset'
            if (insert.getId() != null && insert.getId().equals(existingModel.getId())) {
                // This is the same dataset, no duplicate name issue for update operation
                return;
            }

            // Now we know there's another dataset with the same name
            Field[] fields = Models.class.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true); // Make private fields accessible
                if ("name".equals(field.getName()) && field.isAnnotationPresent(CheckDuplicate.class)) {
                    // If the field is 'name' and is marked with CheckDuplicate annotation
                    CheckDuplicate annotation = field.getAnnotation(CheckDuplicate.class);
                    throw new Exception("重复的模型名称: " + insert.getName() + ". " + annotation.message());
                }
            }
        }
    }

    @Override
    public List<Map<String, String>> exportModels(String path, String uuid) throws Exception {
        List<Map<String, String>> results = new ArrayList<>();
        //根据path得到源文件所在桶名
        String srcBucketName = path.substring(0, path.indexOf("/"));
        //构建目标目录路径
        String username = SecurityUtils.getLoginUser().getUsername();
        String srcDir = path.substring(path.indexOf("/") + 1);
        String targetDir = "models/" + username + "/" + uuid + "/";
        // 递归拷贝整个原目录到目标目录下
        minioUtil.copyDirectory(srcBucketName, srcDir, bucketName, targetDir);
        List<Item> movedItems = minioUtil.getAllObjectsByPrefix(bucketName, targetDir, true);
        for (Item movedItem : movedItems) {
            if (!movedItem.isDir() && movedItem.size() > 0) { // 检查是否为非目录且文件大小大于0
                Map<String, String> result = new HashMap<>();
                String url = movedItem.objectName();
                String fileName = extractFileName(url);
                // 获取文件大小并转换为可读形式
                long sizeInBytes = movedItem.size();
                String formattedSize = FileUtil.formatFileSize(sizeInBytes); // 格式化文件大小
                result.put("fileName", fileName);
                result.put("url", url); // objectName根据实际情况定义
                result.put("fileSize", formattedSize);
                results.add(result);
            }
        }
        return results;
    }

    private String extractFileName(String urlStr) {
        return urlStr.substring(urlStr.lastIndexOf('/') + 1);
    }

    @Override
    public String newCreateModel(ModelsVo modelsVo) {
        try {
            String token = gitService.checkoutToken();
            LoginUser loginUser = SecurityUtils.getLoginUser();
            String ci4sUsername = loginUser.getUsername();
            String gitLinkUsername = loginUser.getSysUser().getUserName();
            String gitLinkPassword = loginUser.getSysUser().getOriginPassword();
            Map<String, Object> userInfo = getUserInfo(ci4sUsername, gitLinkPassword);
            String decryptGitLinkPassword = decrypt(gitLinkPassword);
            Integer userId = (Integer) userInfo.get("user_id");

            ci4sUsername = Boolean.TRUE.equals(modelsVo.getIsPublic()) ? Constant.Item_Public : ci4sUsername;
            String repositoryName = modelsVo.getIdentifier() == null ? ci4sUsername + "_model_" + DateUtils.dateTimeNow() : modelsVo.getIdentifier();
            ModelDependency1 modelDependency = new ModelDependency1();
            List<ModelDependency1> oldModelDependencys = modelDependency1Dao.queryModelDependency(modelsVo.getName(), null, gitLinkUsername);
            if (oldModelDependencys != null && !oldModelDependencys.isEmpty()) {
                throw new RuntimeException("创建模型失败:项目名称已被使用");
            }

            //新建模型
            // 拼接project
            GitProjectVo gitProjectVo = new GitProjectVo();
            gitProjectVo.setRepositoryName(repositoryName);
            gitProjectVo.setName(Constant.Topic_model + "-" + modelsVo.getName());
            gitProjectVo.setDescription(modelsVo.getDescription());
            gitProjectVo.setPrivate(!modelsVo.getIsPublic());
            gitProjectVo.setUserId(userId);
            gitProjectVo.setProjectCategoryId(projectCategoryId);

            // 创建项目
            Map project = gitService.createProject(token, gitProjectVo);
            Integer gitlinIid = (Integer) project.get("id");
            if (gitlinIid == null) {
                throw new RuntimeException("创建模型失败:" + project.get("message"));
            }

            String branchName = modelsVo.getVersion();
            String owner = (String) userInfo.get("login");

            String projectUrl = gitCloneEndpoint + "/" + owner + "/" + repositoryName + ".git";
            String sourcePath = modelsVo.getModelVersionVos().get(0).getUrl();
            String rootPath = localPath + ci4sUsername + "/model/" + gitlinIid + "/" + repositoryName + "/" + branchName;
            String modelPath = rootPath + "/model";
            String metaPath = rootPath + "/metadata";
            String relatePath = ci4sUsername + "/model/" + gitlinIid + "/" + repositoryName + "/" + branchName + "/model";

            // 创建分支
            gitService.createBranch(token, (String) userInfo.get("login"), repositoryName, branchName, "master");
            // 定义标签 标签1：model 标签2：ModelTag 标签3：ModelType
            gitService.createTopic(token, gitlinIid, Constant.Topic_model);
            if (StringUtils.isNotEmpty(modelsVo.getModelTag())) {
                gitService.createTopic(token, gitlinIid, "modeltag-" + modelsVo.getModelTag());
            }
            if (StringUtils.isNotEmpty(modelsVo.getModelType())) {
                gitService.createTopic(token, gitlinIid, "modeltype-" + modelsVo.getModelType());
            }

            dvcUtils.gitClone(rootPath, projectUrl, branchName, gitLinkUsername, decryptGitLinkPassword);

            //干掉目标文件夹
            dvcUtils.deleteDirectory(modelPath);
            dvcUtils.moveFiles(sourcePath, modelPath);

            //拼接生产的元数据后写入yaml文件
            ModelMetaVo modelMetaVo = new ModelMetaVo();
            BeanUtils.copyProperties(modelsVo, modelMetaVo);
            modelMetaVo.setId(gitlinIid);
            modelMetaVo.setCreateBy(String.valueOf(StringUtils.isNotEmpty((String) userInfo.get("nickname")) ? userInfo.get("nickname") : userInfo.get("login")));
            modelMetaVo.setCreateTime(DateUtils.getTime());
            modelMetaVo.setUpdateTime(DateUtils.getTime());
            String projectUrlIp = gitCloneEndpointIp + "/" + owner + "/" + repositoryName + ".git";
            modelMetaVo.setUsage("<pre><code>" +
                    "# 克隆模型配置文件与存储参数到本地\n" +
                    "git clone -b " + branchName + " " + projectUrlIp + "\n" +
                    "# 远程拉取配置文件\n" +
                    "dvc pull\n" +
                    "</code></pre>");
            modelMetaVo.setIdentifier(repositoryName);
            modelMetaVo.setOwner(owner);
            modelMetaVo.setRelativePaths(relatePath);
            File folder = new File(modelPath);
            long folderSize = FileUtil.getFolderSize(folder);
            modelMetaVo.setModelSize(FileUtil.formatFileSize(folderSize));

            Map<String, Object> metaMap = JSON.parseObject(JSON.toJSONString(modelMetaVo), Map.class);
            YamlUtils.generateYamlFile(metaMap, metaPath, "metadata");
            String meta = JSON.toJSONString(modelMetaVo);

            //保存模型依赖
            modelDependency.setRepoId(gitlinIid);
            modelDependency.setIdentifier(repositoryName);
            modelDependency.setModelName(modelsVo.getName());
            modelDependency.setVersion(modelsVo.getVersion());
            modelDependency.setParentModel(modelsVo.getParentModel());
            modelDependency.setOwner(gitLinkUsername);
            modelDependency.setMeta(meta);
            modelDependency.setState(Constant.State_valid);
            modelDependency1Dao.insert(modelDependency);

            String s3Path = bucketName + "/mini-model-management-platform-files/" + ci4sUsername + "/model/" + gitlinIid + "/" + repositoryName + "/" + branchName;
            CompletableFuture.supplyAsync(() -> {
                try {
                    dvcUtils.dvcInit(rootPath);
                    // 配置远程S3地址
                    dvcUtils.dvcRemoteAdd(rootPath, s3Path);
                    dvcUtils.dvcConfigS3Credentials(rootPath, endpoint);
                    dvcUtils.dvcConfigS3Credentials2(rootPath, accessKeyId);
                    dvcUtils.dvcConfigS3Credentials3(rootPath, secretAccessKey);

                    // dvc 跟踪
                    dvcUtils.dvcAdd(rootPath, "model");
                    // git commit
                    dvcUtils.gitCommit(rootPath, "commit from ci4s with " + loginUser.getUsername());
                    dvcUtils.gitPush(rootPath, gitLinkUsername, decryptGitLinkPassword);
                    dvcUtils.dvcPush(rootPath);
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    throw new RuntimeException(e.getMessage());
                }
                return null;
            });

            return "新增模型成功";
        } catch (Exception e) {
            logger.error(e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }

    @Override
    public String newCreateVersion(ModelsVo modelsVo) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String ci4sUsername = loginUser.getUsername();
        String gitLinkUsername = loginUser.getSysUser().getUserName();
        String gitLinkPassword = loginUser.getSysUser().getOriginPassword();
        Map<String, Object> userInfo = getUserInfo(ci4sUsername, gitLinkPassword);
        gitLinkPassword = decrypt(gitLinkPassword);

        ci4sUsername = Boolean.TRUE.equals(modelsVo.getIsPublic()) ? Constant.Item_Public : loginUser.getUsername();
        String repositoryName = modelsVo.getIdentifier() == null ? ci4sUsername + "_model_" + DateUtils.dateTimeNow() : modelsVo.getIdentifier();
        ModelDependency1 modelDependency = new ModelDependency1();
        List<ModelDependency1> oldModelDependencys = modelDependency1Dao.queryModelDependency(modelsVo.getName(), modelsVo.getId(), gitLinkUsername);
        if (oldModelDependencys != null && !oldModelDependencys.isEmpty()) {
            if (oldModelDependencys.stream().anyMatch(oldModelDependency -> oldModelDependency.getVersion().equals(modelsVo.getVersion()))) {
                throw new Exception("模型版本已存在，请勿重复创建");
            }
            //新建版本
            String branchName = modelsVo.getVersion();
            String owner = (String) userInfo.get("login");

            String projectUrl = gitCloneEndpoint + "/" + owner + "/" + repositoryName + ".git";
            String rootPath = localPath + ci4sUsername + "/model/" + modelsVo.getId() + "/" + repositoryName + "/" + branchName;
            String modelPath = rootPath + "/model";
            String metaPath = rootPath + "/metadata";
            String relatePath = ci4sUsername + "/model/" + modelsVo.getId() + "/" + repositoryName + "/" + branchName + "/model";

            //干掉目标文件夹
            dvcUtils.deleteDirectory(rootPath);
            FileUtil.renameFile(rootPath, rootPath + "_deleted_" + System.currentTimeMillis());

            //克隆
            dvcUtils.gitClone(rootPath, projectUrl, oldModelDependencys.get(0).getVersion(), gitLinkUsername, gitLinkPassword);
            dvcUtils.refreshRemoteBranches(rootPath, gitLinkUsername, gitLinkPassword, oldModelDependencys.get(0).getVersion());

            //查询项目信息
            String token = gitService.checkoutToken();
            Map projectDetail = gitService.getProjectDetail(owner, repositoryName, token);

            //检查是否存在本地重名分支，有的话干掉
            dvcUtils.deleteLocalBranch(rootPath, branchName);
            // 创建本地分支
            dvcUtils.createLocalBranchBasedOnMaster(rootPath, branchName);
            //dvc checkout
            dvcUtils.dvcPull(rootPath);
            dvcUtils.dvcCheckout(rootPath);
            //干掉目标文件夹
            dvcUtils.deleteDirectory(modelPath);

            ModelMetaVo modelMetaVo = new ModelMetaVo();
            ModelDependency1 buildingModel = new ModelDependency1();
            if (modelsVo.getModelVersionVos() != null && modelsVo.getModelVersionVos().size() != 0) {
                String sourcePath = modelsVo.getModelVersionVos().get(0).getUrl();

                if (Constant.Source_Hand_Export.equals(modelsVo.getModelSource())) {
                    String bucketName = sourcePath.split("/")[0];
                    String root_path = sourcePath.substring(bucketName.length() + 1);
                    minioUtil.downloadFiles(bucketName, root_path, modelPath);

                    HashMap<String, TrainTaskDepency> map = new HashMap<>();
                    map.put("trainTask", modelsVo.getTrainTask());
                    buildingModel = modelDependency1Dao.queryByTrainTask(JSON.toJSONString(map));
                } else {
                    dvcUtils.moveFiles(sourcePath, modelPath);

                    buildingModel = modelDependency1Dao.getBuildingModel(modelsVo.getId(), modelsVo.getIdentifier(), modelsVo.getVersion());
                }
            }

            if (buildingModel != null) {
                modelMetaVo = JSON.parseObject(buildingModel.getMeta(), ModelMetaVo.class);
                //获取指标
                transMetrics(modelMetaVo);
            }

            //拼接生产的元数据后写入yaml文件
            BeansUtils.copyPropertiesIgnoreNull(modelsVo, modelMetaVo);
            modelMetaVo.setCreateBy(String.valueOf(StringUtils.isNotEmpty((String) userInfo.get("nickname")) ? userInfo.get("nickname") : userInfo.get("login")));
            modelMetaVo.setCreateTime(DateUtils.getTime());
            modelMetaVo.setUpdateTime(DateUtils.getTime());
            modelMetaVo.setUsage("<pre><code>" +
                    "# 克隆模型配置文件与存储参数到本地\n" +
                    "git clone -b " + branchName + " " + projectUrl + "\n" +
                    "# 远程拉取配置文件\n" +
                    "dvc pull\n" +
                    "</code></pre>");
            modelMetaVo.setIdentifier(repositoryName);
            modelMetaVo.setOwner(owner);
            modelMetaVo.setDescription((String) projectDetail.get("description"));
            List<Map> topics = (List<Map>) projectDetail.get("topics");
            if (topics != null && topics.size() > 0) {
                for (Map topic : topics) {
                    String topicName = (String) topic.get("name");
                    if (topicName.startsWith("modeltag-")) {
                        modelMetaVo.setModelTag(topicName.substring("modeltag-".length()));
                    }
                    if (topicName.startsWith("modeltype-")) {
                        modelMetaVo.setModelType(topicName.substring("modeltype-".length()));
                    }
                }
            }

            modelMetaVo.setRelativePaths(relatePath);
            File folder = new File(modelPath);
            long folderSize = FileUtil.getFolderSize(folder);
            modelMetaVo.setModelSize(FileUtil.formatFileSize(folderSize));

            Map<String, Object> metaMap = JSON.parseObject(JSON.toJSONString(modelMetaVo), Map.class);
            YamlUtils.generateYamlFile(metaMap, metaPath, "metadata");
            String meta = JSON.toJSONString(modelMetaVo);

            //保存指标
            Map<String, Object> metrics = modelMetaVo.getMetrics();
            YamlUtils.generateYamlFile(metrics, rootPath, "metrics");
            //保存参数
            Map<String, Object> params = modelMetaVo.getParams();
            YamlUtils.generateYamlFile(params, rootPath, "params");

            if (buildingModel != null) {
                if (Constant.Source_Hand_Export.equals(modelsVo.getModelSource())) {
                    //保存模型依赖
                    BeansUtils.copyPropertiesIgnoreNull(buildingModel, modelDependency);
                    modelDependency.setState(Constant.State_valid);
                    modelDependency.setModelName(modelsVo.getName());
                    modelDependency.setVersion(modelsVo.getVersion());
                    modelDependency.setRepoId(modelsVo.getId());
                    modelDependency.setIdentifier(repositoryName);
                    modelDependency.setMeta(meta);
                    modelDependency1Dao.insert(modelDependency);
                } else {
                    //更新模型依赖
                    modelDependency1Dao.updateState(modelsVo.getId(), modelsVo.getIdentifier(), modelsVo.getVersion(), meta, Constant.State_valid);
                }
            } else {
                //保存模型依赖
                modelDependency.setRepoId(modelsVo.getId());
                modelDependency.setIdentifier(repositoryName);
                modelDependency.setModelName(modelsVo.getName());
                modelDependency.setVersion(modelsVo.getVersion());
                modelDependency.setParentModel(modelsVo.getParentModel());
                modelDependency.setOwner(gitLinkUsername);
                modelDependency.setMeta(meta);
                modelDependency.setState(Constant.State_valid);
                modelDependency1Dao.insert(modelDependency);
            }

            // 配置远程S3地址
            String s3Path = bucketName + "/mini-model-management-platform-files/" + ci4sUsername + "/model/" + modelsVo.getId() + "/" + repositoryName + "/" + branchName;
            dvcUtils.dvcRemoteAdd(rootPath, s3Path);
            dvcUtils.dvcConfigS3Credentials(rootPath, endpoint);
            dvcUtils.dvcConfigS3Credentials2(rootPath, accessKeyId);
            dvcUtils.dvcConfigS3Credentials3(rootPath, secretAccessKey);
            // dvc 跟踪
            dvcUtils.dvcAdd(rootPath, "model");
            dvcUtils.pushNewBranchToRemote(rootPath, gitLinkUsername, gitLinkPassword, branchName);

            //dvc push 到远程S3
            dvcUtils.dvcPush(rootPath);

//            CompletableFuture.supplyAsync(() -> {
//                try {
//                    //dvc push 到远程S3
//                    dvcUtils.dvcPush(rootPath);
//                } catch (Exception e) {
//                    throw new RuntimeException(e);
//                }
//                return null;
//            });
        }
        return "新增模型版本成功";
    }


    @Override
    public List<Map<String, String>> uploadModelLocal(MultipartFile[] files, String uuid) throws Exception {
        List<Map<String, String>> results = new ArrayList<>();

        for (MultipartFile file : files) {
            // 构建objectName
            String username = SecurityUtils.getLoginUser().getUsername();
            String fileName = file.getOriginalFilename();
            String path = localPath + "temp/" + username + "/models/" + uuid;
            long sizeInBytes = file.getSize();
            String formattedSize = FileUtil.formatFileSize(sizeInBytes);
            File targetFile = new File(path, file.getOriginalFilename());
            // 确保目录存在
            targetFile.getParentFile().mkdirs();
            // 保存文件到目标路径
            FileUtils.copyInputStreamToFile(file.getInputStream(), targetFile);
            // 返回上传文件的路径
            Map<String, String> result = new HashMap<>();
            result.put("fileName", fileName);
            result.put("url", path); // objectName根据实际情况定义
            result.put("fileSize", formattedSize);
            results.add(result);
        }
        return results;
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadAllModelFilesNew(String name, String identifier, Integer id, String version, Boolean isPublic) throws Exception {
        // 命令行操作 git clone 项目地址
        String ci4sUsername = isPublic ? Constant.Item_Public : SecurityUtils.getLoginUser().getUsername();

        String localPath1 = localPath + ci4sUsername + "/model/" + id + "/" + identifier + "/" + version;
        // 打包 data 文件夹
        String dataFolderPath = localPath1 + "/model";
        String zipFilePath = localPath1 + "/model.zip";
        try (FileOutputStream fos = new FileOutputStream(zipFilePath);
             ZipOutputStream zos = new ZipOutputStream(fos)) {
            Path sourcePath = Paths.get(dataFolderPath);
            Files.walk(sourcePath).forEach(path -> {
                if (!Files.isDirectory(path)) {
                    ZipEntry zipEntry = new ZipEntry(sourcePath.relativize(path).toString());
                    try {
                        zos.putNextEntry(zipEntry);
                        Files.copy(path, zos);
                        zos.closeEntry();
                    } catch (IOException e) {
                        throw new RuntimeException("Error while zipping: " + path, e);
                    }
                }
            });
        }

        // 返回压缩文件的输入流
        File zipFile = new File(zipFilePath);
        InputStreamResource resource = new InputStreamResource(new FileInputStream(zipFile));

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + name + "_" + version + "_model.zip")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .contentLength(zipFile.length())
                .body(resource);
    }

    @Override
    public Page<ModelsVo> newPersonalQueryByPage(ModelsVo modelsVo, PageRequest pageRequest) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String ci4sUsername = loginUser.getUsername();
        String gitLinkPassword = loginUser.getSysUser().getOriginPassword();

        Map<String, Object> userInfo = getUserInfo(ci4sUsername, gitLinkPassword);
        String token = (String) userInfo.get("token");

        //拼接查询url
        String modelTagName = modelsVo.getModelTag();
        String modelTypeName = modelsVo.getModelType();

        String url = gitendpoint + "/api/users/" + userInfo.get("login") + "/projects.json?category=manage&sort_direction=praises_count&sort_by=desc";

        String name = modelsVo.getName();
        if (StringUtils.isNotEmpty(name)) {
            url = url + "&search=" + name;
        }

        String req = httpUtils.sendGetWithToken(url, null, token);
        Map<String, Object> stringObjectMap = JacksonUtil.parseJSONStr2Map(req);
        List<Map<String, Object>> projects = (List<Map<String, Object>>) stringObjectMap.get("projects");
        List<ModelsVo> collect = convert(projects, Constant.Topic_model, modelTagName, modelTypeName);
        List<ModelsVo> result = collect.stream().sorted(Comparator.comparing(ModelsVo::getPraisesCount).reversed()).skip((pageRequest.getPageNumber()) * pageRequest.getPageSize()).limit(pageRequest.getPageSize()).
                collect(Collectors.toList());
        return new PageImpl<>(result, pageRequest, collect.size());
    }

    @Override
    public Page<ModelsVo> newPubilcQueryByPage(ModelsVo modelsVo, PageRequest pageRequest) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String ci4sUsername = loginUser.getUsername();
        String gitLinkPassword = loginUser.getSysUser().getOriginPassword();

        Map<String, Object> userInfo = getUserInfo(ci4sUsername, gitLinkPassword);
        String token = (String) userInfo.get("token");

        String modelTagName = modelsVo.getModelTag();
        String modelTypeName = modelsVo.getModelType();

        //拼接查询url
        String url = gitendpoint + "/api/projects.json?sort_direction=praises_count&sort_by=desc&category_id=" + projectCategoryId;

        String name = modelsVo.getName();
        if (StringUtils.isNotEmpty(name)) {
            url = url + "&search=" + name;
        }

        String req = httpUtils.sendGetWithToken(url, null, token);
        Map<String, Object> stringObjectMap = JacksonUtil.parseJSONStr2Map(req);
        List<Map<String, Object>> projects = (List<Map<String, Object>>) stringObjectMap.get("projects");
        List<ModelsVo> collect = convert(projects, Constant.Topic_model, modelTagName, modelTypeName);
        List<ModelsVo> result = collect.stream().sorted(Comparator.comparing(ModelsVo::getPraisesCount).reversed()).skip((pageRequest.getPageNumber()) * pageRequest.getPageSize()).limit(pageRequest.getPageSize()).
                collect(Collectors.toList());
        return new PageImpl<>(result, pageRequest, collect.size());
    }

    @Override
    public Page<Map<String, Object>> queryVersions(PageRequest pageRequest, String identifier, String owner, String type) throws Exception {
        String token = gitService.checkoutToken();
        List<Map<String, Object>> collect = gitService.getBrancheList(token, owner, identifier);
        List<Map<String, Object>> result = collect.stream()
                .filter(branch -> !"master".equals(branch.get("name")))
                .skip((pageRequest.getPageNumber()) * pageRequest.getPageSize()).limit(pageRequest.getPageSize())
                .collect(Collectors.toList());

        for (Map<String, Object> branch : result) {
            String meta = modelDependency1Dao.getMeta(identifier, owner, (String) branch.get("name"));
            if (StringUtils.isNotEmpty(meta)) {
                ModelMetaVo modelMetaVo = JSON.parseObject(meta, ModelMetaVo.class);
                if (Constant.Source_Add.equals(modelMetaVo.getModelSource())) {
                    branch.put("params", null);
                    branch.put("params_names", new ArrayList<>());
                    branch.put("metrics", null);
                    branch.put("metrics_names", new ArrayList<>());
                } else {
                    if (modelMetaVo.getParams() != null) {
                        HashMap<String, Object> params = modelMetaVo.getParams();
                        branch.put("params", params);

                        ArrayList<String> params_names = new ArrayList<>();
                        for (String key : params.keySet()) {
                            params_names.add(key);
                        }
                        branch.put("params_names", params_names);
                    }
                    if (modelMetaVo.getMetrics() != null) {
                        HashMap<String, Object> metrics = modelMetaVo.getMetrics();
                        if (Constant.Type_Train.equals(type)) {
                            Map<String, Object> trainMetrics = (Map<String, Object>) metrics.get(Constant.Type_Train);
                            branch.put("metrics", trainMetrics);

                            ArrayList<String> metrics_names = new ArrayList<>();
                            if (trainMetrics != null) {
                                for (String key : trainMetrics.keySet()) {
                                    metrics_names.add(key);
                                }
                            }
                            branch.put("metrics_names", metrics_names);
                        } else {
                            Map<String, Object> evaluateMetrics = (Map<String, Object>) metrics.get(Constant.Type_Evaluate);
                            ArrayList<String> metrics_names = new ArrayList<>();
                            if (evaluateMetrics != null) {
                                for (String key : evaluateMetrics.keySet()) {
                                    metrics_names.add(key);
                                }
                            }
                            branch.put("metrics_names", metrics_names);
                            branch.put("metrics", evaluateMetrics);
                        }
                    }
                }
            }
        }
        return new PageImpl<>(result, pageRequest, collect.size() - 1);
    }

    @Override
    public Map<String, Object> queryVersionsMetrics(QueryModelMetricsVo queryModelMetricsVo) {
        Map<String, Object> batchMetrics = new HashMap<>();
        List<String> needMetrics = queryModelMetricsVo.getMetrics();

        for (String needMetric : needMetrics) {
            ArrayList<Map<String, Object>> result = new ArrayList<>();

            for (String version : queryModelMetricsVo.getVersions()) {
                ModelDependency1 modelDependency1 = modelDependency1Dao.queryByRepoAndVersion(queryModelMetricsVo.getRepoId(), queryModelMetricsVo.getIdentifier(), version);
                ModelMetaVo modelMetaVo = JSON.parseObject(modelDependency1.getMeta(), ModelMetaVo.class);
                HashMap<String, Object> metrics = modelMetaVo.getMetricsParams();

                if (Constant.Type_Train.equals(queryModelMetricsVo.getType())) {
                    List<List<Map<String, Object>>> tarinDetail = (List<List<Map<String, Object>>>) metrics.get("tarinDetail");
                    if (tarinDetail != null && tarinDetail.size() > 0) {
                        List<Map<String, Object>> trainMetrics = tarinDetail.get(0);
                        for (Map<String, Object> trainMetric : trainMetrics) {
                            if (needMetric.equals(trainMetric.get("name"))) {
                                trainMetric.put("version", version);
                                result.add(trainMetric);
                            }
                        }
                    }
                } else {
                    List<List<Map<String, Object>>> evaluateDetail = (List<List<Map<String, Object>>>) metrics.get("evaluateDetail");
                    if (evaluateDetail != null && evaluateDetail.size() > 0) {
                        List<Map<String, Object>> evaluateMetrics = evaluateDetail.get(0);
                        for (Map<String, Object> evaluateMetric : evaluateMetrics) {
                            if (needMetric.equals(evaluateMetric.get("name"))) {
                                evaluateMetric.put("version", version);
                                result.add(evaluateMetric);
                            }
                        }
                    }
                }
            }
            batchMetrics.put(needMetric, result);
        }

        return batchMetrics;
    }

    @Override
    public List<Map<String, Object>> getVersionList(String identifier, String owner) throws Exception {
        String token = gitService.checkoutToken();
        List<Map<String, Object>> branchList = gitService.getBrancheList(token, owner, identifier);
        return branchList.stream()
                .filter(branch -> !"master".equals(branch.get("name")))
                .collect(Collectors.toList());
    }

    @Override
    public ModelsVo getModelDetail(Integer id, String identifier, String owner, String version, Boolean isPublic) throws Exception {
        String ci4sUsername = isPublic ? Constant.Item_Public : SecurityUtils.getLoginUser().getUsername();

        if (StringUtils.isEmpty(version)) {
            List<Map<String, Object>> versionList = this.getVersionList(identifier, owner);
            if (versionList.size() == 0) {
                throw new Exception("模型文件不存在");
            }
            version = (String) versionList.get(0).get("name");
        }

        // git pull操作，然后读取里面的文件列表，列出每个文件的大小和名称，封装成MAP
        List<Map<String, Object>> fileDetailsAfterGitPull = FileUtil.getFiles(localPath + ci4sUsername + "/model/" + id + "/" + identifier + "/" + version + "/model");
        Map<String, Object> stringObjectMap = YamlUtils.loadYamlFile(localPath + ci4sUsername + "/model/" + id + "/" + identifier + "/" + version + "/metadata/metadata.yaml");

        String jsonString = JSON.toJSONString(stringObjectMap);
        ModelsVo modelsVo = JSON.parseObject(jsonString, ModelsVo.class);

        if (!fileDetailsAfterGitPull.isEmpty()) {
            List<VersionVo> versionVos = new ArrayList<>();
            for (Map<String, Object> fileDetail : fileDetailsAfterGitPull) {
                VersionVo versionVo = new VersionVo();
                versionVo.setUrl((String) fileDetail.get("filePath"));
                versionVo.setFileName((String) fileDetail.get("fileName"));
                long size = (long) fileDetail.get("size");
                versionVo.setFileSize(FileUtil.formatFileSize(size));
                versionVos.add(versionVo);
            }
            modelsVo.setModelVersionVos(versionVos);
        } else {
            throw new Exception("版本数据为空");
        }

        String token = gitService.checkoutToken();
        String url = gitendpoint + "/api/" + owner + "/" + identifier + "/detail.json";
        String req = httpUtils.sendGetWithToken(url, null, token);
        Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req);
        modelsVo.setPraisesCount((Integer) reqMap.get("praises_count"));
        modelsVo.setPraised((Boolean) reqMap.get("praised"));
        return modelsVo;
    }

    @Override
    public ModelDependency1TreeVo getModelDependencyTree(Integer repoId, String identifier, String version) {
        ModelDependency1 modelDependency1 = modelDependency1Dao.queryByRepoAndVersion(repoId, identifier, version);
        ModelDependency1TreeVo modelDependency1TreeVo = new ModelDependency1TreeVo();
        BeanUtils.copyProperties(modelDependency1, modelDependency1TreeVo);

        ModelMetaVo modelMetaVo = JSON.parseObject(modelDependency1TreeVo.getMeta(), ModelMetaVo.class);
        modelDependency1TreeVo.setModelMeta(modelMetaVo);

        //递归查询父模型
        getParentModel(modelDependency1TreeVo);
        //递归查询子模型
        getChildModel(modelDependency1TreeVo);

        return modelDependency1TreeVo;
    }

    @Override
    public void deleteModel(Integer repoId, String identifier, String owner, Boolean isPublic) throws Exception {
        List<AssetWorkflow> assetWorkflow = assetWorkflowDao.getAssetWorkflow(Long.valueOf(repoId), Constant.Asset_Type_Model, null);
        if (assetWorkflow != null && !assetWorkflow.isEmpty()) {
            String workflows = String.join(",", assetWorkflow.stream().map(AssetWorkflow::getWorkflowName).collect(Collectors.toSet()));
            throw new Exception("该模型被流水线：" + workflows + "使用，不能删除，请先删除流水线");
        }

        HashMap<String, String> queryMap = new HashMap<>();
        queryMap.put("id", String.valueOf(repoId));
        List<Ray> rayList = rayDao.queryByModelId(JSON.toJSONString(queryMap));
        if (rayList != null && !rayList.isEmpty()) {
            String rays = String.join(",", rayList.stream().map(Ray::getName).collect(Collectors.toSet()));
            throw new Exception("该模型被超参数自动寻优：" + rays + "使用，不能删除，请先删除超参数自动寻优");
        }

        List<ActiveLearn> activeLearnList = activeLearnDao.queryByModelId(JSON.toJSONString(queryMap));
        if (activeLearnList != null && !activeLearnList.isEmpty()) {
            String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
            throw new Exception("该模型被主动学习：" + activeLearns + "使用，不能删除，请先删除主动学习");
        }

        List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByModelId(JSON.toJSONString(queryMap));
        if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
            String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
            throw new Exception("该模型被开发环境：" + devEnvironments + "使用，不能删除，请先删除开发环境");
        }

        List<String> serviceVersionList = serviceDao.queryByModelId(JSON.toJSONString(queryMap));
        if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
            String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
            throw new Exception("该模型被服务版本：" + serviceVersions + "使用，不能删除，请先删除服务版本");
        }

        String token = gitService.checkoutToken();
        gitService.deleteProject(token, owner, identifier);
        //删除模型依赖
        modelDependency1Dao.deleteModel(repoId, identifier, owner, null);

        HashMap<String, Object> map = new HashMap<>();
        map.put("repoId", repoId);
        map.put("identifier", identifier);
        String parentModel = JSON.toJSONString(map);
        modelDependency1Dao.deleteModelDependency(parentModel);

        String ci4sUsername = isPublic ? Constant.Item_Public : SecurityUtils.getLoginUser().getUsername();
        dvcUtils.deleteDirectory(localPath + "/" + ci4sUsername + "/model/" + repoId + "/" + identifier);
    }

    @Override
    public void deleteVersion(Integer repoId, String identifier, String owner, String version, String relativePath) throws Exception {
        List<AssetWorkflow> assetWorkflow = assetWorkflowDao.getAssetWorkflow(Long.valueOf(repoId), Constant.Asset_Type_Model, version);
        if (assetWorkflow != null && !assetWorkflow.isEmpty()) {
            String workflows = String.join(",", assetWorkflow.stream().map(AssetWorkflow::getWorkflowName).collect(Collectors.toSet()));
            throw new Exception("该模型版本被流水线：" + workflows + "使用，不能删除，请先删除流水线");
        }

        HashMap<String, String> queryMap = new HashMap<>();
        queryMap.put("id", String.valueOf(repoId));
        queryMap.put("version", version);
        List<Ray> rayList = rayDao.queryByModelId(JSON.toJSONString(queryMap));
        if (rayList != null && !rayList.isEmpty()) {
            String rays = String.join(",", rayList.stream().map(Ray::getName).collect(Collectors.toSet()));
            throw new Exception("该模型版本被超参数自动寻优：" + rays + "使用，不能删除，请先删除超参数自动寻优");
        }

        List<ActiveLearn> activeLearnList = activeLearnDao.queryByModelId(JSON.toJSONString(queryMap));
        if (activeLearnList != null && !activeLearnList.isEmpty()) {
            String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
            throw new Exception("该模型版本被主动学习：" + activeLearns + "使用，不能删除，请先删除主动学习");
        }

        List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByModelId(JSON.toJSONString(queryMap));
        if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
            String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
            throw new Exception("该模型版本被开发环境：" + devEnvironments + "使用，不能删除，请先删除开发环境");
        }

        List<String> serviceVersionList = serviceDao.queryByModelId(JSON.toJSONString(queryMap));
        if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
            String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
            throw new Exception("该模型版本被服务版本：" + serviceVersions + "使用，不能删除，请先删除服务版本");
        }

        String token = gitService.checkoutToken();
        String rootPath = Paths.get(localPath + "/" + relativePath).getParent().toString();
        gitService.deleteBranch(token, owner, identifier, version, rootPath);
        //删除模型依赖
        modelDependency1Dao.deleteModel(repoId, identifier, owner, version);
        HashMap<String, Object> map = new HashMap<>();
        map.put("repoId", repoId);
        map.put("identifier", identifier);
        map.put("version", version);
        String parentModel = JSON.toJSONString(map);
        modelDependency1Dao.deleteModelDependency(parentModel);
        dvcUtils.deleteDirectory(rootPath);
        FileUtil.renameFile(rootPath, rootPath + "_deleted_" + System.currentTimeMillis());
    }

    @Override
    public Map<String, Object> getVersionsCompare(QueryModelMetricsVo queryModelMetricsVo) throws Exception {
        Map<String, Object> result = new HashMap<>();
        List<String> versions = queryModelMetricsVo.getVersions();

        ModelDependency1 modelDependency1 = modelDependency1Dao.queryByRepoAndVersion(queryModelMetricsVo.getRepoId(), queryModelMetricsVo.getIdentifier(), versions.get(0));
        ModelMetaVo modelMetaVo1 = JSON.parseObject(modelDependency1.getMeta(), ModelMetaVo.class);

        ModelDependency1 modelDependency2 = modelDependency1Dao.queryByRepoAndVersion(queryModelMetricsVo.getRepoId(), queryModelMetricsVo.getIdentifier(), versions.get(1));
        ModelMetaVo modelMetaVo2 = JSON.parseObject(modelDependency2.getMeta(), ModelMetaVo.class);

        Map<String, String> compareMap = ConvertUtil.compareObjects(modelMetaVo1, modelMetaVo2);

        result.put("version1", modelMetaVo1);
        result.put("version2", modelMetaVo2);
        result.put("differences", compareMap);
        return result;
    }

    @Override
    public String praise(Integer id) throws Exception {
        String token = gitService.checkoutToken();
        String url = gitendpoint + "/api/projects/" + id + "/praise_tread/like.json";
        httpUtils.sendPostWithToken(url, null, token, null);
        return "点赞成功";
    }

    @Override
    public String unpraise(Integer id) throws Exception {
        String token = gitService.checkoutToken();
        String url = gitendpoint + "/api/projects/" + id + "/praise_tread/unlike.json";
        httpUtils.sendDeleteWithToken(url, null, token, null);
        return "取消点赞成功";
    }

    @Override
    public List<ModelsVo> convert(List<Map<String, Object>> lst, String modelTopic, String modelTagName, String modelTypeName) {
        if (lst != null && lst.size() > 0) {
            List<ModelsVo> result = new ArrayList<>();
            List<ModelsVo> newModelVos = ConvertUtil.convertListMapToObjectList(lst, ModelsVo.class);

            for (ModelsVo newModelVo : newModelVos) {
                Map<String, Object> map = lst.stream()
                        .filter(m -> m.get("id").equals(newModelVo.getId()))
                        .findFirst()
                        .orElse(null);

                if (map != null) {
                    List<Map<String, Object>> topics = (List<Map<String, Object>>) map.get("topics");
                    String modelTopicName = null;
                    String modelTag = null;
                    String modelType = null;
                    for (Map<String, Object> topic : topics) {
                        String topicName = (String) topic.get("name");
                        if (modelTopic.equals(topicName)) {
                            modelTopicName = (String) topic.get("name");
                        }
                        if (topicName.startsWith("modeltag-")) {
                            modelTag = topicName.substring("modeltag-".length());
                        }
                        if (topicName.startsWith("modeltype-")) {
                            modelType = topicName.substring("modeltype-".length());
                        }
                    }

                    if (modelTopicName != null && modelTopic.equals(modelTopicName)) {
                        if (StringUtils.isNotEmpty(modelTagName) && !modelTagName.toLowerCase().equals(modelTag)) {
                            continue;
                        }
                        if (StringUtils.isNotEmpty(modelTypeName) && !modelTypeName.toLowerCase().equals(modelType)) {
                            continue;
                        }
                        newModelVo.setModelTag(modelTagName);
                        newModelVo.setModelType(modelTypeName);
                        Map<String, Object> author = (Map<String, Object>) map.get("author");
                        newModelVo.setCreateBy((String) author.get("name"));
                        newModelVo.setOwner((String) author.get("login"));
                        String name = (String) map.get("name");
                        newModelVo.setName(name.substring((modelTopic + "-").length()));
                        result.add(newModelVo);
                    }
                }
            }
            return result;
        }
        return new ArrayList<>();
    }

    void getParentModel(ModelDependency1TreeVo modelDependency1TreeVo) {
        if (modelDependency1TreeVo.getParentModel() != null) {
            ModelDependency1 parent = JSON.parseObject(modelDependency1TreeVo.getParentModel(), ModelDependency1.class);
            ModelDependency1 parentModel = modelDependency1Dao.queryByRepoAndVersion(parent.getRepoId(), parent.getIdentifier(), parent.getVersion());

            if (parentModel != null) {
                ModelDependency1TreeVo parentModelVo = new ModelDependency1TreeVo();
                BeanUtils.copyProperties(parentModel, parentModelVo);

                ModelMetaVo modelMetaVo = JSON.parseObject(parentModelVo.getMeta(), ModelMetaVo.class);
                parentModelVo.setModelMeta(modelMetaVo);
                modelDependency1TreeVo.setParentModelVo(parentModelVo);

                if (parentModel.getParentModel() != null) {
                    getParentModel(parentModelVo);
                }
            }
        }
    }

    void getChildModel(ModelDependency1TreeVo modelDependency1TreeVo) {
        List<ModelDependency1TreeVo> childModelList = new ArrayList<>();

        HashMap<String, Object> map = new HashMap<>();
        map.put("repoId", modelDependency1TreeVo.getRepoId());
        map.put("identifier", modelDependency1TreeVo.getIdentifier());
        map.put("version", modelDependency1TreeVo.getVersion());
        String model = JSON.toJSONString(map);

        List<ModelDependency1> children = modelDependency1Dao.queryByParentModel(model);
        if (children != null && !children.isEmpty()) {
            for (ModelDependency1 modelDependency1 : children) {
                ModelDependency1TreeVo modelDependency1TreeVo1 = new ModelDependency1TreeVo();
                BeanUtils.copyProperties(modelDependency1, modelDependency1TreeVo1);

                ModelMetaVo modelMetaVo = JSON.parseObject(modelDependency1TreeVo1.getMeta(), ModelMetaVo.class);
                modelDependency1TreeVo1.setModelMeta(modelMetaVo);

                getChildModel(modelDependency1TreeVo1);
                childModelList.add(modelDependency1TreeVo1);
            }
        }
        modelDependency1TreeVo.setChildModelList(childModelList);
    }

    Map<String, Object> getUserInfo(String ci4sUsername, String gitLinkPassword) throws Exception {
        Jedis jedis = new Jedis(redisHost, redisPort);
        String userReq = jedis.get(ci4sUsername + "_gitUserInfo");
        if (userReq == null) {
            gitService.login(ci4sUsername, gitLinkPassword);
            userReq = jedis.get(ci4sUsername + "_gitUserInfo");
        }
        Map<String, Object> userInfo = JsonUtils.jsonToMap(userReq);

        String token = gitService.checkoutToken();
        userInfo.put("token", token);
        return userInfo;
    }

    void transMetrics(ModelMetaVo modelMetaVo) throws Exception {
        HashMap<String, Object> result = new HashMap<>();
        HashMap<String, Object> train = new HashMap<>();
        HashMap<String, Object> evaluate = new HashMap<>();

        List<List<Map<String, Object>>> trainBatchMetrics = new ArrayList<>();
        List<List<Map<String, Object>>> evaluateBatchMetrics = new ArrayList<>();

        HashMap<String, Object> metrics = modelMetaVo.getMetricsParams();
        JSONArray trainMetrics = (JSONArray) metrics.get("train");
        if (trainMetrics != null) {
            for (int i = 0; i < trainMetrics.size(); i++) {
                JSONObject jsonObject = trainMetrics.getJSONObject(i);
                String runId = jsonObject.getString("run_id");
                List<InsMetricInfoVo> expTrainInfos = aimsService.getExpInfos1(true, modelMetaVo.getTrainTask().getExperimentId(), runId);
                for (InsMetricInfoVo expTrainInfo : expTrainInfos) {
                    Map metrics1 = expTrainInfo.getMetrics();
                    train.putAll(metrics1);
                }

                // 记录训练指标详情
                HashMap<String, Object> map = aimsService.queryMetricsParams(runId);
                if (map != null && !map.isEmpty()) {
                    List<Map<String, Object>> batchMetric = aimsService.getBatchMetric((String) map.get("run_hash"), (String) map.get("params"));
                    trainBatchMetrics.add(batchMetric);
                }
            }
            result.put("train", train);
            metrics.put("tarinDetail", trainBatchMetrics);
        }

        JSONArray testMetrics = (JSONArray) metrics.get("evaluate");
        if (testMetrics != null) {
            for (int i = 0; i < testMetrics.size(); i++) {
                JSONObject jsonObject = testMetrics.getJSONObject(i);
                String runId = jsonObject.getString("run_id");
                List<InsMetricInfoVo> expTestInfos = aimsService.getExpInfos1(false, modelMetaVo.getTrainTask().getExperimentId(), runId);
                for (InsMetricInfoVo expTestInfo : expTestInfos) {
                    Map metrics1 = expTestInfo.getMetrics();
                    evaluate.putAll(metrics1);
                }

                // 记录验证指标详情
                HashMap<String, Object> map = aimsService.queryMetricsParams(runId);
                if (map != null && !map.isEmpty()) {
                    List<Map<String, Object>> batchMetric = aimsService.getBatchMetric((String) map.get("run_hash"), (String) map.get("params"));
                    evaluateBatchMetrics.add(batchMetric);
                }

            }
            result.put("evaluate", evaluate);
            metrics.put("evaluateDetail", evaluateBatchMetrics);

        }
        modelMetaVo.setMetrics(result);
        modelMetaVo.setMetricsParams(metrics);
    }

}
