package com.ruoyi.platform.service.impl;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.domain.Ray;
import com.ruoyi.platform.domain.RayIns;
import com.ruoyi.platform.mapper.RayDao;
import com.ruoyi.platform.mapper.RayInsDao;
import com.ruoyi.platform.service.RayInsService;
import com.ruoyi.platform.service.RayService;
import com.ruoyi.platform.service.ResourceOccupyService;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.JsonUtils;
import com.ruoyi.platform.vo.RayParamVo;
import com.ruoyi.platform.vo.RayVo;
import com.ruoyi.system.api.constant.Constant;
import org.apache.commons.collections4.MapUtils;
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.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("rayService")
public class RayServiceImpl implements RayService {

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

    @Resource
    private ResourceOccupyService resourceOccupyService;

    @Resource
    private RayDao rayDao;
    @Resource
    private RayInsDao rayInsDao;
    @Resource
    private RayInsService rayInsService;

    @Override
    public Page<Ray> queryByPage(String name, PageRequest pageRequest) {
        long total = rayDao.count(name);
        List<Ray> rays = rayDao.queryByPage(name, pageRequest);
        return new PageImpl<>(rays, pageRequest, total);
    }

    @Override
    public Ray save(RayVo rayVo) throws Exception {
        if (rayVo.getName().length() > 64) {
            throw new RuntimeException("实验名称大于最大长度");
        }
        Ray rayByName = rayDao.getRayByName(rayVo.getName());
        if (rayByName != null) {
            throw new RuntimeException("实验名称已存在");
        }

        Ray ray = new Ray();
        BeanUtils.copyProperties(rayVo, ray);
        String username = SecurityUtils.getLoginUser().getUsername();
        ray.setCreateBy(username);
        ray.setUpdateBy(username);
        ray.setDataset(JacksonUtil.toJSONString(rayVo.getDataset()));
        ray.setCodeConfig(JacksonUtil.toJSONString(rayVo.getCodeConfig()));
        ray.setModel(JacksonUtil.toJSONString(rayVo.getModel()));
        ray.setImage(JacksonUtil.toJSONString(rayVo.getImage()));
        ray.setParameters(JacksonUtil.toJSONString(rayVo.getParameters()));
        ray.setPointsToEvaluate(JacksonUtil.toJSONString(rayVo.getPointsToEvaluate()));
        rayDao.save(ray);
        return ray;
    }

    @Override
    public String edit(RayVo rayVo) throws Exception {
        Ray oldRay = rayDao.getRayByName(rayVo.getName());
        if (oldRay != null && !oldRay.getId().equals(rayVo.getId())) {
            throw new RuntimeException("实验名称已存在");
        }
        Ray ray = new Ray();
        BeanUtils.copyProperties(rayVo, ray);
        ray.setUpdateBy(SecurityUtils.getLoginUser().getUsername());
        ray.setParameters(JacksonUtil.toJSONString(rayVo.getParameters()));
        ray.setPointsToEvaluate(JacksonUtil.toJSONString(rayVo.getPointsToEvaluate()));
        ray.setDataset(JacksonUtil.toJSONString(rayVo.getDataset()));
        ray.setCodeConfig(JacksonUtil.toJSONString(rayVo.getCodeConfig()));
        ray.setModel(JacksonUtil.toJSONString(rayVo.getModel()));
        ray.setImage(JacksonUtil.toJSONString(rayVo.getImage()));
        rayDao.edit(ray);
        return "修改成功";
    }

    @Override
    public RayVo getRayDetail(Long id) throws IOException {
        Ray ray = rayDao.getRayById(id);
        RayVo rayVo = new RayVo();
        BeanUtils.copyProperties(ray, rayVo);

        Gson gson = new Gson();
        Type listType = new TypeToken<List<Map<String, Object>>>() {
        }.getType();
        if (StringUtils.isNotEmpty(ray.getParameters())) {
            rayVo.setParameters(gson.fromJson(ray.getParameters(), listType));
        }
        if (StringUtils.isNotEmpty(ray.getPointsToEvaluate())) {
            rayVo.setPointsToEvaluate(gson.fromJson(ray.getPointsToEvaluate(), listType));
        }
        if (StringUtils.isNotEmpty(ray.getDataset())) {
            rayVo.setDataset(JsonUtils.jsonToMap(ray.getDataset()));
        }
        if (StringUtils.isNotEmpty(ray.getCodeConfig())) {
            rayVo.setCodeConfig(JsonUtils.jsonToMap(ray.getCodeConfig()));
        }
        if (StringUtils.isNotEmpty(ray.getModel())) {
            rayVo.setModel(JsonUtils.jsonToMap(ray.getModel()));
        }
        if (StringUtils.isNotEmpty(ray.getImage())) {
            rayVo.setImage(JsonUtils.jsonToMap(ray.getImage()));
        }
        return rayVo;
    }

    @Override
    @Transactional
    public String delete(Long id) throws Exception {
        Ray ray = rayDao.getRayById(id);
        if (ray == null) {
            throw new Exception("实验不存在");
        }
        String username = SecurityUtils.getLoginUser().getUsername();
        String createBy = ray.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) {
            throw new Exception("无权限删除该实验");
        }
        List<RayIns> insList = rayInsDao.getByRayId(id);
        if (!insList.isEmpty()) {
            throw new Exception("该实验存在实例，无法删除");
        }
        ray.setState(Constant.State_invalid);
        resourceOccupyService.deleteTaskState(Constant.TaskType_Ray, id, null);
        return rayDao.edit(ray) > 0 ? "删除成功" : "删除失败";
    }

    @Override
    public String runRayIns(Long id) throws Exception {
        Ray ray = rayDao.getRayById(id);
        if (ray == null) {
            throw new Exception("自动超参数寻优配置不存在");
        }

        // 记录开始扣积分
        if (resourceOccupyService.haveResource(ray.getComputingResourceId(), 1)) {
            RayParamVo rayParamVo = new RayParamVo();
            BeanUtils.copyProperties(ray, rayParamVo);
            rayParamVo.setCodeConfig(JsonUtils.jsonToMap(ray.getCodeConfig()));
            rayParamVo.setDataset(JsonUtils.jsonToMap(ray.getDataset()));
            rayParamVo.setModel(JsonUtils.jsonToMap(ray.getModel()));
            rayParamVo.setImage(JsonUtils.jsonToMap(ray.getImage()));
            String param = JsonUtils.getConvertParam(rayParamVo);

            // 调argo转换接口
            try {
                String convertRes = HttpUtils.sendPost(argoUrl + convertRay, param);
                if (convertRes == null || StringUtils.isEmpty(convertRes)) {
                    throw new RuntimeException("转换流水线失败");
                }

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

                // 组装运行接口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");

                // 插入记录到实验实例表
                RayIns rayIns = new RayIns();
                rayIns.setRayId(id);
                rayIns.setArgoInsNs((String) metadata.get("namespace"));
                rayIns.setArgoInsName((String) metadata.get("name"));
                rayIns.setParam(param);
                rayIns.setStatus(Constant.Pending);
                //替换argoInsName
                String outputString = JsonUtils.mapToJson(output);
                rayIns.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 = output2.get("path").replace("{{workflow.name}}", (String) metadata.get("name")) + "/hpo";
                rayIns.setResultPath(outputPath);
                rayInsDao.insert(rayIns);
                rayInsService.updateRayStatus(id);
                // 记录开始扣除积分
                resourceOccupyService.startDeduce(ray.getComputingResourceId(), 1, Constant.TaskType_Ray, id, rayIns.getId(), null, ray.getName(), null, null);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }
        return "执行成功";
    }
}
