package com.ruoyi.platform.service.impl;

import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.annotations.CheckDuplicate;
import com.ruoyi.platform.domain.AssetWorkflow;
import com.ruoyi.platform.domain.Experiment;
import com.ruoyi.platform.domain.ImageVersion;
import com.ruoyi.platform.domain.Workflow;
import com.ruoyi.platform.mapper.AssetWorkflowDao;
import com.ruoyi.platform.mapper.ImageVersionDao;
import com.ruoyi.platform.mapper.WorkflowDao;
import com.ruoyi.platform.service.ExperimentService;
import com.ruoyi.platform.service.WorkflowService;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.utils.MinioUtil;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.system.api.model.LoginUser;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;

/**
 * DAG workflow(Workflow)表服务实现类
 *
 * @author makejava
 * @since 2023-10-18 14:17:10
 */
@Service("workflowService")
public class WorkflowServiceImpl implements WorkflowService {
    @Resource
    private WorkflowDao workflowDao;
    @Resource
    private ImageVersionDao imageVersionDao;
    @Resource
    private AssetWorkflowDao assetWorkflowDao;

    @Resource
    private ExperimentService experimentService;

    @Value("${argo.url}")
    private String argoUrl;

    @Value("${argo.workflowCopy}")
    private String argoWorkflowCopy;

    private final MinioUtil minioUtil;

    public WorkflowServiceImpl(MinioUtil minioUtil) {
        this.minioUtil = minioUtil;
    }

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public Workflow queryById(Long id) {
        return this.workflowDao.queryById(id);
    }

    public WorkflowDao getWorkflowDao() {
        return workflowDao;
    }

    public void setWorkflowDao(WorkflowDao workflowDao) {
        this.workflowDao = workflowDao;
    }

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

    /**
     * 新增数据
     *
     * @param workflow 实例对象
     * @return 实例对象
     */
    @Override
    public Workflow insert(Workflow workflow) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        checkDeclaredName(workflow);
        workflow.setCreateBy(loginUser.getUsername());
        workflow.setUpdateBy(loginUser.getUsername());
        workflow.setUpdateTime(new Date());
        workflow.setCreateTime(new Date());
        workflow.setState(Constant.State_valid);
        this.workflowDao.insert(workflow);
        saveAssetWorkFlow(workflow);
        return workflow;
    }

    /**
     * 修改数据
     *
     * @param workflow 实例对象
     * @return 实例对象
     */
    @Override
    public Workflow update(Workflow workflow) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        checkDeclaredName(workflow);
        workflow.setUpdateBy(loginUser.getUsername());
        workflow.setUpdateTime(new Date());
        this.workflowDao.update(workflow);
        assetWorkflowDao.deleteByWorkFlowId(workflow.getId());
        saveAssetWorkFlow(workflow);
        if (StringUtils.isNotEmpty(workflow.getGlobalParam())) {
            List<Experiment> experimentList = experimentService.queryByWorkflowId(workflow.getId());
            for (Experiment experiment : experimentList) {
                experiment.setGlobalParam(workflow.getGlobalParam());
                experimentService.update(experiment);
            }
        }
        return this.queryById(workflow.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    @Transactional
    public String removeById(Long id) throws Exception {
        //先根据id提取出对应的流水线
        Workflow workflow = workflowDao.queryById(id);
        if (workflow == null) {
            throw new Exception("流水线不存在");
        }
        //判断权限，只有admin和创建者本身可以删除该流水线
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String username = loginUser.getUsername();
        String createdBy = workflow.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) {
            throw new Exception("无权限删除该流水线");
        }

        //判断这个流水线是否有相关实验存在
        List<Experiment> experimentList = experimentService.queryByWorkflowId(id);
        if (experimentList != null && experimentList.size() > 0) {
            throw new Exception("该流水线存在实验，无法删除");
        }
        workflow.setState(Constant.State_invalid);
        assetWorkflowDao.deleteByWorkFlowId(id);
        return this.workflowDao.update(workflow) > 0 ? "删除成功" : "删除失败";
    }

    @Override
    public Page<Workflow> queryByName(String name) {
        return new PageImpl<>(this.workflowDao.queryByName(name));
    }


    @Override
    public Workflow duplicateWorkflow(Long id) {
        //先去查找数据库中存在的数据
        Workflow workflow = this.queryById(id);
        if (workflow != null) {
            try {
                Workflow duplicateWorkflow = new Workflow();
                duplicateWorkflow.setName(workflow.getName() + "-copy-" + UUID.randomUUID().toString().substring(0, 6));

                if (duplicateWorkflow.getName().length() > 64) {
                    throw new RuntimeException("流水线名称大于最大长度");
                }

                String oldDag = workflow.getDag();
                // 创建请求数据的Json(map)
                Map<String, Object> requestData = new HashMap<>();
                requestData.put("data", oldDag);
                // 发送POST请求到Argo工作流复制接口，并将请求数据转换为JSON
                String req = HttpUtils.sendPost(argoUrl + argoWorkflowCopy, null, JsonUtils.mapToJson(requestData));
                // 检查响应是否为空或无内容
                if (StringUtils.isEmpty(req)) {
                    throw new RuntimeException("工作流复制接口响应内容为空");
                }
                // 将响应的JSON字符串转换为Map对象
                Map<String, Object> runResMap = JsonUtils.jsonToMap(req);
                // 从响应Map中获取"data"的值,也就是日志的值
                String newDag = (String) runResMap.get("data");
                if (newDag == null) {
                    throw new RuntimeException("响应内容为空");
                }
                duplicateWorkflow.setDag(newDag);
                duplicateWorkflow.setDescription(workflow.getDescription());
                duplicateWorkflow.setGlobalParam(workflow.getGlobalParam());
                return this.insert(duplicateWorkflow);
            } catch (Exception e) {
                throw new RuntimeException("复制流水线失败: " + e.getMessage(), e);
            }
        }
        return null;


    }

    public void checkDeclaredName(Workflow insert) throws Exception {
        Workflow existingWorkflow = workflowDao.findByName(insert.getName());
        if (existingWorkflow != null) {
            // 检查找到的流水线是否与要插入的流水线相同
            if (insert.getId() != null && insert.getId().equals(existingWorkflow.getId())) {
                // 这是相同的流水线，更新操作中没有重复名称问题
                return;
            }
            Field[] fields = Workflow.class.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if ("name".equals(field.getName()) && field.isAnnotationPresent(CheckDuplicate.class)) {
                    // 如果字段是“name”并且标记了CheckDuplicate注解
                    CheckDuplicate annotation = field.getAnnotation(CheckDuplicate.class);
                    throw new Exception("重复的流水线名称: " + insert.getName() + ". " + annotation.message());
                }
            }
        }
    }

    public void saveAssetWorkFlow(Workflow workflow) throws Exception {
        if (StringUtils.isNotEmpty(workflow.getDag())) {
            Map<String, Object> dagMap = JsonUtils.jsonToMap(workflow.getDag());
            List<Map<String, Object>> nodes = (List<Map<String, Object>>) dagMap.get("nodes");
            for (Map<String, Object> node : nodes) {
                String componentName = (String) node.get("component_name");
                // 自定义执行
                if ("self-command-execute".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                }
                // 代码拉取组件
                else if ("git-clone".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存代码-流水线关系
                    saveCodeWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
                // 模型训练
                else if ("model-train".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存数据集-流水线关系
                    saveDatasetWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                    //保存模型-流水线关系
                    saveModelWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
                // 分布式训练
                else if ("distributed-model-train".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存数据集-流水线关系
                    saveDatasetWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                    //保存模型-流水线关系
                    saveModelWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
                // 模型测试
                else if ("model-evaluate".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存数据集-流水线关系
                    saveDatasetWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                    //保存模型-流水线关系
                    saveModelWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
                // 数据预处理
                else if ("general-data-process".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存数据集-流水线关系
                    saveDatasetWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
                // 模型导出
                else if ("model-export".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存模型-流水线关系
                    saveModelWorkFlow1((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
                // 数据集导出
                else if ("dataset-export".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存数据集-流水线关系
                    saveDatasetWorkFlow1((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
                // 模型部署
                else if ("model-deploy".equals(componentName)) {
                    //保存镜像-流水线关系
                    saveImageWorkFlow((String) node.get("image"), workflow.getId(), workflow.getName());
                    //保存模型-流水线关系
                    saveModelWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                    //保存服务-流水线关系
                    saveServiceWorkFlow((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                    //保存镜像-流水线关系
                    saveImageWorkFlow1((String) node.get("in_parameters"), workflow.getId(), workflow.getName());
                }
            }
        }
    }

    public void saveImageWorkFlow(String image, Long workflowId, String workflowName) throws Exception {
        try {
            String[] imageSplit = image.split(":");
            String tagName = imageSplit[1];
            ImageVersion imageVersion = imageVersionDao.queryByUrl(image);
            insertAssetWorkFlow(workflowId, workflowName, Long.valueOf(imageVersion.getImageId()), tagName, Constant.Asset_Type_Image);
        } catch (Exception e) {
            throw new Exception("镜像参数有误：" + e);
        }
    }

    public void saveImageWorkFlow1(String inParameters, Long workflowId, String workflowName) throws Exception {
        try {
            Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
            Map<String, Object> image = (Map<String, Object>) paramMap.get("--image");
            saveImageWorkFlow((String) image.get("value"), workflowId, workflowName);
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }

    public void saveCodeWorkFlow(String inParameters, Long workflowId, String workflowName) throws Exception {
        try {
            Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
            Map<String, Object> code_config = (Map<String, Object>) paramMap.get("--code_config");
            String value = (String) code_config.get("value");
            Map<String, Object> valueMap = JsonUtils.jsonToMap(value);
            insertAssetWorkFlow(workflowId, workflowName, Long.valueOf((Integer) valueMap.get("id")), null, Constant.Asset_Type_Code);
        } catch (IOException e) {
            throw new Exception("代码配置参数有误：" + e);
        }
    }

    public void saveDatasetWorkFlow(String inParameters, Long workflowId, String workflowName) throws Exception {
        try {
            Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
            Map<String, Object> dataset = (Map<String, Object>) paramMap.get("--dataset");
            String value = (String) dataset.get("value");
            if (!value.startsWith("{{")) {
                Map<String, Object> valueMap = JsonUtils.jsonToMap(value);
                insertAssetWorkFlow(workflowId, workflowName, Long.valueOf((String) valueMap.get("id")), (String) valueMap.get("version"), Constant.Asset_Type_Dataset);
            }
        } catch (Exception e) {
            throw new Exception("数据集参数有误：" + e);
        }
    }

    public void saveDatasetWorkFlow1(String inParameters, Long workflowId, String workflowName) throws Exception {
        try {
            Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
            Map<String, Object> dataset = (Map<String, Object>) paramMap.get("--dataset_id");
            String value = (String) dataset.get("value");
            Map<String, Object> valueMap = JsonUtils.jsonToMap(value);
            insertAssetWorkFlow(workflowId, workflowName, Long.valueOf((String) valueMap.get("id")), null, Constant.Asset_Type_Dataset);
        } catch (Exception e) {
            throw new Exception("数据集参数有误：" + e);
        }
    }

    public void saveModelWorkFlow(String inParameters, Long workflowId, String workflowName) throws Exception {
        try {
            Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
            Map<String, Object> model = (Map<String, Object>) paramMap.get("--model_name");
            String value = (String) model.get("value");
            if (StringUtils.isNotEmpty(value) && !value.startsWith("{{")) {
                Map<String, Object> valueMap = JsonUtils.jsonToMap(value);
                insertAssetWorkFlow(workflowId, workflowName, Long.valueOf((String) valueMap.get("id")), (String) valueMap.get("version"), Constant.Asset_Type_Model);
            }
        } catch (Exception e) {
            throw new Exception("模型参数有误：" + e);
        }
    }

    public void saveModelWorkFlow1(String inParameters, Long workflowId, String workflowName) throws Exception {
        try {
            Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
            Map<String, Object> model = (Map<String, Object>) paramMap.get("--model_id");
            String value = (String) model.get("value");
            Map<String, Object> valueMap = JsonUtils.jsonToMap(value);
            insertAssetWorkFlow(workflowId, workflowName, Long.valueOf((String) valueMap.get("id")), null, Constant.Asset_Type_Model);
        } catch (Exception e) {
            throw new Exception("模型参数有误：" + e);
        }
    }

    public void saveServiceWorkFlow(String inParameters, Long workflowId, String workflowName) throws Exception {
        try {
            Map<String, Object> paramMap = JsonUtils.jsonToMap(inParameters);
            Map<String, Object> service = (Map<String, Object>) paramMap.get("--service_name");
            String value = (String) service.get("value");
            Map<String, Object> valueMap = JsonUtils.jsonToMap(value);
            insertAssetWorkFlow(workflowId, workflowName, Long.valueOf((Integer) valueMap.get("id")), null, Constant.Asset_Type_Service);
        } catch (Exception e) {
            throw new Exception("服务参数有误：" + e);
        }
    }

    public void insertAssetWorkFlow(Long workflowId, String workflowName, Long assetId, String version, String type) {
        AssetWorkflow assetWorkflow = new AssetWorkflow();
        assetWorkflow.setWorkflowId(workflowId);
        assetWorkflow.setWorkflowName(workflowName);
        assetWorkflow.setAssetId(assetId);
        assetWorkflow.setAssetVersion(version);
        assetWorkflow.setType(type);
        assetWorkflowDao.insert(assetWorkflow);
    }

}
