package com.ruoyi.platform.service.impl;

import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.constant.Constant;
import com.ruoyi.platform.domain.AutoMl;
import com.ruoyi.platform.domain.AutoMlIns;
import com.ruoyi.platform.mapper.AutoMlDao;
import com.ruoyi.platform.mapper.AutoMlInsDao;
import com.ruoyi.platform.service.AutoMlInsService;
import com.ruoyi.platform.service.AutoMlService;
import com.ruoyi.platform.utils.FileUtil;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.vo.AutoMlParamVo;
import com.ruoyi.platform.vo.AutoMlVo;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
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.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("autoMLService")
public class AutoMlServiceImpl implements AutoMlService {
    @Value("${git.localPath}")
    String localPath;

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

    @Value("${minio.endpoint}")
    private String minioEndpoint;

    @Resource
    private AutoMlDao autoMlDao;

    @Resource
    private AutoMlInsDao autoMlInsDao;

    @Resource
    private AutoMlInsService autoMlInsService;

    @Override
    public Page<AutoMl> queryByPage(String mlName, PageRequest pageRequest) {
        long total = autoMlDao.count(mlName);
        List<AutoMl> autoMls = autoMlDao.queryByPage(mlName, pageRequest);
        return new PageImpl<>(autoMls, pageRequest, total);
    }

    @Override
    public AutoMl save(AutoMlVo autoMlVo) throws Exception {
        AutoMl autoMlByName = autoMlDao.getAutoMlByName(autoMlVo.getMlName());
        if (autoMlByName != null) {
            throw new RuntimeException("实验名称已存在");
        }
        AutoMl autoMl = new AutoMl();
        BeanUtils.copyProperties(autoMlVo, autoMl);
        String username = SecurityUtils.getLoginUser().getUsername();
        autoMl.setCreateBy(username);
        autoMl.setUpdateBy(username);
        String datasetJson = JacksonUtil.toJSONString(autoMlVo.getDataset());
        autoMl.setDataset(datasetJson);
        autoMlDao.save(autoMl);
        return autoMl;
    }

    @Override
    public String edit(AutoMlVo autoMlVo) throws Exception {
        AutoMl oldAutoMl = autoMlDao.getAutoMlByName(autoMlVo.getMlName());
        if (oldAutoMl != null && !oldAutoMl.getId().equals(autoMlVo.getId())) {
            throw new RuntimeException("实验名称已存在");
        }
        AutoMl autoMl = new AutoMl();
        BeanUtils.copyProperties(autoMlVo, autoMl);
        String username = SecurityUtils.getLoginUser().getUsername();
        autoMl.setUpdateBy(username);
        String datasetJson = JacksonUtil.toJSONString(autoMlVo.getDataset());
        autoMl.setDataset(datasetJson);

        autoMlDao.edit(autoMl);
        return "修改成功";
    }

    @Override
    public String delete(Long id) {
        AutoMl autoMl = autoMlDao.getAutoMlById(id);
        if (autoMl == null) {
            throw new RuntimeException("实验不存在");
        }

        String username = SecurityUtils.getLoginUser().getUsername();
        String createBy = autoMl.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) {
            throw new RuntimeException("无权限删除该实验");
        }

        autoMl.setState(Constant.State_invalid);
        return autoMlDao.edit(autoMl) > 0 ? "删除成功" : "删除失败";
    }

    @Override
    public AutoMlVo getAutoMlDetail(Long id) throws IOException {
        AutoMl autoMl = autoMlDao.getAutoMlById(id);
        AutoMlVo autoMlVo = new AutoMlVo();
        BeanUtils.copyProperties(autoMl, autoMlVo);
        if (StringUtils.isNotEmpty(autoMl.getDataset())) {
            autoMlVo.setDataset(JsonUtils.jsonToMap(autoMl.getDataset()));
        }
        return autoMlVo;
    }

    @Override
    public Map<String, String> upload(MultipartFile file, String uuid) throws Exception {
        Map<String, String> result = new HashMap<>();

        String username = SecurityUtils.getLoginUser().getUsername();
        String fileName = file.getOriginalFilename();
        String path = localPath + "temp/" + username + "/automl_data/" + 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);
        // 返回上传文件的路径
        result.put("fileName", fileName);
        result.put("url", path); // objectName根据实际情况定义
        result.put("fileSize", formattedSize);
        return result;
    }

    @Override
    public String runAutoMlIns(Long id) throws Exception {
        AutoMl autoMl = autoMlDao.getAutoMlById(id);
        if (autoMl == null) {
            throw new Exception("自动机器学习配置不存在");
        }

        AutoMlParamVo autoMlParam = new AutoMlParamVo();
        BeanUtils.copyProperties(autoMl, autoMlParam);
        autoMlParam.setDataset(JsonUtils.jsonToMap(autoMl.getDataset()));
        String param = JsonUtils.objectToJson(autoMlParam);
        // 调argo转换接口
        try {
            String convertRes = HttpUtils.sendPost(argoUrl + convertAutoML, param);
            if (convertRes == null || StringUtils.isEmpty(convertRes)) {
                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("Failed to run workflow.");
            }
            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("Failed to run workflow.");
            }
            Map<String, Object> metadata = (Map<String, Object>) data.get("metadata");
            // 插入记录到实验实例表
            AutoMlIns autoMlIns = new AutoMlIns();
            autoMlIns.setAutoMlId(autoMl.getId());
            autoMlIns.setArgoInsNs((String) metadata.get("namespace"));
            autoMlIns.setArgoInsName((String) metadata.get("name"));
            autoMlIns.setParam(param);
            autoMlIns.setStatus(Constant.Pending);
            //替换argoInsName
            String outputString = JsonUtils.mapToJson(output);
            autoMlIns.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 outputPath = minioEndpoint + "/" + output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name")) + "/";
            autoMlIns.setModelPath(outputPath + "save_model.joblib");
            if (Constant.AutoMl_Classification.equals(autoMl.getTaskType())) {
                autoMlIns.setImgPath(outputPath + "Auto-sklearn_accuracy_over_time.png" + "," + outputPath + "Train_Confusion_Matrix.png" + "," + outputPath + "Test_Confusion_Matrix.png");
            } else {
                autoMlIns.setImgPath(outputPath + "Auto-sklearn_accuracy_over_time.png" + "," + outputPath + "regression.png");
            }
            autoMlIns.setResultPath(outputPath + "result.txt");
            String seed = autoMl.getSeed() != null ? String.valueOf(autoMl.getSeed()) : "1";
            autoMlIns.setRunHistoryPath(outputPath + "smac3-output/run_" + seed + "/runhistory.json");
            autoMlInsDao.insert(autoMlIns);
            autoMlInsService.updateAutoMlStatus(id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return "执行成功";
    }
}
