package com.ruoyi.platform.service.impl;

import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.domain.MachineLearn;
import com.ruoyi.platform.domain.MachineLearnIns;
import com.ruoyi.platform.mapper.MachineLearnDao;
import com.ruoyi.platform.mapper.MachineLearnInsDao;
import com.ruoyi.platform.service.MachineLearnInsService;
import com.ruoyi.platform.service.MachineLearnService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.vo.AutoMlParamVo;
import com.ruoyi.platform.vo.TextClassificationParamVo;
import com.ruoyi.platform.vo.VideoClassificationParamVo;
import com.ruoyi.system.api.constant.Constant;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class MachineLearnServiceImpl implements MachineLearnService {
    @Value("${argo.url}")
    private String argoUrl;
    @Value("${argo.convertAutoML}")
    String convertAutoML;
    @Value("${argo.convertTextClassification}")
    String convertTextClassification;
    @Value("${argo.convertVideoClassification}")
    String convertVideoClassification;
    @Value("${argo.workflowRun}")
    private String argoWorkflowRun;
    @Value("${minio.endpointIp}")
    private String minioEndpoint;

    @Resource
    private MachineLearnDao machineLearnDao;
    @Resource
    private MachineLearnInsDao machineLearnInsDao;
    @Resource
    private ResourceOccupyService resourceOccupyService;
    @Autowired
    private MachineLearnInsService machineLearnInsService;

    @Override
    public Page<MachineLearn> queryByPage(String name, String type, PageRequest pageRequest) {
        long total = machineLearnDao.count(name, type);
        List<MachineLearn> autoMls = machineLearnDao.queryByPage(name, type, pageRequest);
        return new PageImpl<>(autoMls, pageRequest, total);
    }

    @Override
    public MachineLearn add(MachineLearn machineLearn) {
        if (machineLearn.getName().length() > 64) {
            throw new RuntimeException("实验名称大于最大长度");
        }
        MachineLearn machineLearnByName = machineLearnDao.getMachineLearnByName(machineLearn.getName());
        if (machineLearnByName != null) {
            throw new RuntimeException("实验名称已存在");
        }
        String username = SecurityUtils.getLoginUser().getUsername();
        machineLearn.setCreateBy(username);
        machineLearn.setUpdateBy(username);
        machineLearnDao.save(machineLearn);
        return machineLearn;
    }

    @Override
    public String edit(MachineLearn machineLearn) {
        MachineLearn machineLearnByName = machineLearnDao.getMachineLearnByName(machineLearn.getName());
        if (machineLearnByName != null && !machineLearnByName.getId().equals(machineLearn.getId())) {
            throw new RuntimeException("实验名称已存在");
        }
        machineLearn.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
        machineLearnDao.edit(machineLearn);
        return "修改成功";
    }

    @Override
    public MachineLearn getMLDetail(Long id) {
        return machineLearnDao.getMachineLearnById(id);
    }

    @Override
    @Transactional
    public String delete(Long id) {
        MachineLearn machineLearn = machineLearnDao.getMachineLearnById(id);
        if (machineLearn == null) {
            throw new RuntimeException("实验不存在");
        }
        String username = SecurityUtils.getLoginUser().getUsername();
        String createBy = machineLearn.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) {
            throw new RuntimeException("无权限删除该实验");
        }

        List<MachineLearnIns> insList = machineLearnInsDao.getByMachineLearnId(machineLearn.getId());
        if (!insList.isEmpty()) {
            throw new RuntimeException("该实验存在实例，无法删除");
        }

        machineLearn.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
        machineLearn.setState(Constant.State_invalid);
        resourceOccupyService.deleteTaskState(Constant.TaskType_ML, id, null);
        return machineLearnDao.edit(machineLearn) > 0 ? "删除成功" : "删除失败";
    }

    @Override
    @Transactional
    public String runMachineLearn(Long id) throws Exception {
        MachineLearn machineLearn = machineLearnDao.getMachineLearnById(id);
        if (machineLearn == null) {
            throw new Exception("自动机器学习配置不存在");
        }
        // 调argo转换接口
        try {
            String convertRes = null;
            String param = null;
            String modelType = null;
            String taskType = null;
            Integer seed = null;
            Integer computingResourceId = null;
            switch (machineLearn.getType()) {
                case Constant.ML_CSV: {
                    AutoMlParamVo paramVo = JsonUtils.jsonToObject(machineLearn.getParam(), AutoMlParamVo.class);
                    taskType = paramVo.getTaskType();
                    seed = paramVo.getSeed();
                    param = JsonUtils.getConvertParam(paramVo);
                    convertRes = HttpUtils.sendPost(argoUrl + convertAutoML, param);
                    break;
                }
                case Constant.ML_TextClassification: {
                    TextClassificationParamVo paramVo = JsonUtils.jsonToObject(machineLearn.getParam(), TextClassificationParamVo.class);
                    modelType = paramVo.getModelType();
                    computingResourceId = paramVo.getComputingResourceId();
                    if (resourceOccupyService.haveResource(computingResourceId, 1)) {
                        param = JsonUtils.getConvertParam(paramVo);
                        convertRes = HttpUtils.sendPost(argoUrl + convertTextClassification, param);
                    }
                    break;
                }
                case Constant.ML_VideoClassification: {
                    VideoClassificationParamVo paramVo = JsonUtils.jsonToObject(machineLearn.getParam(), VideoClassificationParamVo.class);
                    computingResourceId = paramVo.getComputingResourceId();
                    if (resourceOccupyService.haveResource(computingResourceId, 1)) {
                        param = JsonUtils.getConvertParam(paramVo);
                        convertRes = HttpUtils.sendPost(argoUrl + convertVideoClassification, param);
                    }
                    break;
                }
            }

            if (convertRes == null || StringUtils.isEmpty(convertRes)) {
                throw new RuntimeException("转换流水线失败");
            }

            Map<String, Object> convertResMap = JsonUtils.jsonToMap(convertRes);
            if (convertResMap.get("data")== null) {
                throw new RuntimeException("转换流水线失败");
            }

            Map<String, Object> converMap = JsonUtils.jsonToMap(convertRes);
            // 组装运行接口json
            Map<String, Object> output = (Map<String, Object>) converMap.get("output");
            Map<String, Object> runReqMap = new HashMap<>();
            runReqMap.put("data", converMap.get("data"));
            // 调argo运行接口
            String runRes = HttpUtils.sendPost(argoUrl + argoWorkflowRun, JsonUtils.mapToJson(runReqMap));

            if (runRes == null || StringUtils.isEmpty(runRes)) {
                throw new RuntimeException("运行流水线失败");
            }
            Map<String, Object> runResMap = JsonUtils.jsonToMap(runRes);
            Map<String, Object> data = (Map<String, Object>) runResMap.get("data");
            //判断data为空
            if (data == null || MapUtils.isEmpty(data)) {
                throw new RuntimeException("运行流水线失败");
            }
            Map<String, Object> metadata = (Map<String, Object>) data.get("metadata");
            // 插入记录到实验实例表
            MachineLearnIns machineLearnIns = new MachineLearnIns();
            machineLearnIns.setMachineLearnId(id);
            machineLearnIns.setType(machineLearn.getType());
            machineLearnIns.setArgoInsNs((String) metadata.get("namespace"));
            machineLearnIns.setArgoInsName((String) metadata.get("name"));
            machineLearnIns.setParam(param);
            machineLearnIns.setStatus(Constant.Pending);
            //替换argoInsName
            String outputString = JsonUtils.mapToJson(output);
            machineLearnIns.setNodeResult(outputString.replace("{{workflow.name}}", (String) metadata.get("name")));
            Map<String, Object> param_output = (Map<String, Object>) output.get("param_output");
            List output1 = (ArrayList) param_output.values().toArray()[0];
            Map<String, String> output2 = (Map<String, String>) output1.get(0);
            String outputStr = output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name"));
            String outputPath = minioEndpoint + "/" + outputStr + "/";

            switch (machineLearn.getType()) {
                case Constant.ML_CSV: {
                    machineLearnIns.setModelPath(outputPath + "save_model.joblib");
                    if (Constant.AutoMl_Classification.equals(taskType)) {
                        machineLearnIns.setImgPath(outputPath + "Auto-sklearn_metric_over_time.png" + "," + outputPath + "Train_Confusion_Matrix.png" + "," + outputPath + "Test_Confusion_Matrix.png");
                    } else {
                        machineLearnIns.setImgPath(outputPath + "Auto-sklearn_metric_over_time.png" + "," + outputPath + "regression.png");
                    }
                    machineLearnIns.setResultPath(outputPath + "result.txt");
                    String seedStr = seed != null ? String.valueOf(seed) : "1";
                    machineLearnIns.setRunHistoryPath(outputPath + "smac3-output/run_" + seedStr + "/runhistory.json");
                    break;
                }
                case Constant.ML_TextClassification: {
                    machineLearnIns.setModelPath(outputPath + "saved_dict/" + modelType + ".ckpt");
                    machineLearnIns.setRunHistoryPath(outputStr.substring("data/".length()) + "/log/" + modelType);
                    machineLearnIns.setResultPath(outputPath + "log/" + modelType + "/result.txt");
                    break;
                }
                case Constant.ML_VideoClassification: {
                    machineLearnIns.setResultPath(outputStr);
                    machineLearnIns.setRunHistoryPath(outputStr.substring("data/".length()) + "/log");
                    break;
                }
            }
            machineLearnInsDao.insert(machineLearnIns);
            machineLearnInsService.updateMLStatus(id);
            if (computingResourceId != null) {
                // 记录开始扣除积分
                resourceOccupyService.startDeduce(computingResourceId, 1, Constant.TaskType_ML, id, machineLearnIns.getId(), null, machineLearn.getName(), null, null);
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
        return "执行成功";
    }
}
