package com.ruoyi.platform.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.constant.Constant;
import com.ruoyi.platform.domain.ServiceVersion;
import com.ruoyi.platform.mapper.ServiceDao;
import com.ruoyi.platform.service.ServiceService;
import com.ruoyi.platform.utils.ConvertUtil;
import com.ruoyi.platform.utils.HttpUtils;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.vo.serviceVos.ServiceCodeConfigVo;
import com.ruoyi.platform.vo.serviceVos.ServiceModelVo;
import com.ruoyi.platform.vo.serviceVos.ServiceVersionVo;
import com.ruoyi.system.api.model.LoginUser;
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 javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service("serviceService")
public class ServiceServiceImpl implements ServiceService {

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

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

    @Resource
    private ServiceDao serviceDao;

    @Override
    public Page<com.ruoyi.platform.domain.Service> queryByPageService(com.ruoyi.platform.domain.Service service, PageRequest pageRequest) {
        long total = serviceDao.countService(service);
        List<com.ruoyi.platform.domain.Service> services = serviceDao.queryByPageService(service, pageRequest);
        return new PageImpl<>(services, pageRequest, total);
    }

    @Override
    public Page<ServiceVersionVo> queryByPageServiceVersion(ServiceVersion serviceVersion, int page, int size) {
        PageRequest pageRequest;
        if (StringUtils.isNotEmpty(serviceVersion.getRunState())) {
            pageRequest = PageRequest.of(page, Integer.MAX_VALUE);
            List<ServiceVersion> serviceVersions = serviceDao.queryByPageServiceVersion(serviceVersion, pageRequest);

            List<String> deploymentNames = serviceVersions.stream().map(ServiceVersion::getDeploymentName).collect(Collectors.toList());
            Map<String, String> runStates = getRunState(deploymentNames);
            if (runStates != null) {
                updateRunState(runStates, serviceVersions);
            }

            List<ServiceVersion> serviceVersionList = serviceVersions.stream().filter(serviceVersion1 -> {
                String runState;
                if (runStates != null) {
                    runState = runStates.get(serviceVersion1.getDeploymentName());
                } else {
                    runState = serviceVersion1.getRunState();
                }
                return serviceVersion.getRunState().equals(runState);
            }).collect(Collectors.toList());

            List<ServiceVersionVo> result = getServiceVersionVoList(serviceVersionList);

            PageRequest.of(page, size);
            return new PageImpl<>(result, pageRequest, result.size());
        } else {
            pageRequest = PageRequest.of(page, size);
            long total = serviceDao.countServiceVersion(serviceVersion);
            List<ServiceVersion> serviceVersions = serviceDao.queryByPageServiceVersion(serviceVersion, pageRequest);

            List<String> deploymentNames = serviceVersions.stream().map(ServiceVersion::getDeploymentName).collect(Collectors.toList());
            Map<String, String> runStates = getRunState(deploymentNames);
            if (runStates != null) {
                updateRunState(runStates, serviceVersions);
            }
            List<ServiceVersionVo> result = getServiceVersionVoList(serviceVersions);

            return new PageImpl<>(result, pageRequest, total);
        }
    }

    @Override
    public com.ruoyi.platform.domain.Service addService(com.ruoyi.platform.domain.Service service) {
        com.ruoyi.platform.domain.Service serviceByName = serviceDao.getServiceByName(service.getServiceName());
        if (serviceByName != null) {
            throw new RuntimeException("服务名称已存在，无法新增");
        }

        LoginUser loginUser = SecurityUtils.getLoginUser();
        service.setCreateBy(loginUser.getUsername());
        service.setUpdateBy(loginUser.getUsername());
        serviceDao.insertService(service);
        return service;
    }

    @Override
    public ServiceVersion addServiceVersion(ServiceVersionVo serviceVersionVo) {
        ServiceVersion svByVersion = serviceDao.getSvByVersion(serviceVersionVo.getVersion(), serviceVersionVo.getServiceId());
        if (svByVersion != null) {
            throw new RuntimeException("服务版本已存在，无法新增");
        }

        ServiceVersion serviceVersion = getServiceVersion(serviceVersionVo);
        LoginUser loginUser = SecurityUtils.getLoginUser();
        serviceVersion.setCreateBy(loginUser.getUsername());
        serviceVersion.setUpdateBy(loginUser.getUsername());
        serviceVersion.setRunState(Constant.Pending);
        serviceDao.insertServiceVersion(serviceVersion);
        runServiceVersion(serviceVersion.getId());
        return serviceVersion;
    }

    @Override
    public com.ruoyi.platform.domain.Service editService(com.ruoyi.platform.domain.Service service) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        service.setUpdateBy(loginUser.getUsername());
        serviceDao.updateService(service);
        return serviceDao.getServiceById(service.getId());
    }

    @Override
    public String editServiceVersion(ServiceVersionVo serviceVersionVo) {
        ServiceVersion serviceVersion = getServiceVersion(serviceVersionVo);

        ServiceVersion oldServiceVersion = serviceDao.getServiceVersionById(serviceVersionVo.getId());
        if (!oldServiceVersion.getReplicas().equals(serviceVersionVo.getReplicas()) || !oldServiceVersion.getResource().equals(serviceVersionVo.getResource())
                || serviceVersionVo.getRerun()) {
            updateServiceVersion(serviceVersion);
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        serviceVersion.setUpdateBy(loginUser.getUsername());
        serviceDao.updateServiceVersion(serviceVersion);
        return "修改成功";
    }

    @Override
    public com.ruoyi.platform.domain.Service getService(Long id) {
        return serviceDao.getServiceById(id);
    }

    @Override
    public ServiceVersionVo getServiceVersion(Long id) {
        ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
        ServiceVersionVo serviceVersionVo = getServiceVersionVo(serviceVersion);
        com.ruoyi.platform.domain.Service service = serviceDao.getServiceById(serviceVersion.getServiceId());
        serviceVersionVo.setServiceName(service.getServiceName());
        return serviceVersionVo;
    }

    @Override
    public Map<String, Object> serviceVersionCompare(Long id1, Long id2) throws IllegalAccessException {
        HashMap<String, Object> result = new HashMap<>();

        ServiceVersion serviceVersion1 = serviceDao.getServiceVersionById(id1);
        ServiceVersion serviceVersion2 = serviceDao.getServiceVersionById(id2);
        com.ruoyi.platform.domain.Service service = serviceDao.getServiceById(serviceVersion1.getServiceId());

        ServiceVersionVo serviceVersionVo1 = getServiceVersionVo(serviceVersion1);
        ServiceVersionVo serviceVersionVo2 = getServiceVersionVo(serviceVersion2);
        serviceVersionVo1.setServiceName(service.getServiceName());
        serviceVersionVo2.setServiceName(service.getServiceName());

        Map<String, String> compareMap = ConvertUtil.compareObjects(serviceVersion1, serviceVersion2);

        result.put("version1", serviceVersionVo1);
        result.put("version2", serviceVersionVo2);
        result.put("differences", compareMap);

        return result;
    }

    @Override
    public String deleteService(Long id) {
        com.ruoyi.platform.domain.Service service = serviceDao.getServiceById(id);
        if (service == null) {
            throw new RuntimeException("服务不存在");
        }

        LoginUser loginUser = SecurityUtils.getLoginUser();
        String username = loginUser.getUsername();
        String createBy = service.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createBy))) {
            throw new RuntimeException("无权限删除该服务");
        }

        ServiceVersion serviceVersion = new ServiceVersion();
        serviceVersion.setServiceId(id);
        long versionCount = serviceDao.countServiceVersion(serviceVersion);
        if (versionCount != 0) {
            throw new RuntimeException("该服务下还有版本，不能删除");
        }

        service.setState(Constant.State_invalid);
        return serviceDao.updateService(service) > 0 ? "删除成功" : "删除失败";
    }

    @Override
    public String deleteServiceVersion(Long id) {
        ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
        serviceVersion.setState(Constant.State_invalid);
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("deployment_name", serviceVersion.getDeploymentName());
        paramMap.put("svc_name", serviceVersion.getSvcName());
        String req = HttpUtils.sendPost(argoUrl + modelService + "/delete", JSON.toJSONString(paramMap));
        if (StringUtils.isNotEmpty(req)) {
            Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req);
            if ((Integer) reqMap.get("code") == 200) {
                return serviceDao.updateServiceVersion(serviceVersion) > 0 ? "删除成功" : "删除失败";
            }
        }
        return "删除失败";
    }

    @Override
    public String runServiceVersion(Long id) {
        ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
        com.ruoyi.platform.domain.Service service = serviceDao.getServiceById(serviceVersion.getServiceId());
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("service_name", service.getServiceName());
        paramMap.put("description", serviceVersion.getDescription());
        paramMap.put("resource", serviceVersion.getResource());
        paramMap.put("mount_path", serviceVersion.getMountPath());
        paramMap.put("replicas", serviceVersion.getReplicas());
        paramMap.put("env", JSONObject.parseObject(serviceVersion.getEnvVariables()));
        paramMap.put("code_config", JSONObject.parseObject(serviceVersion.getCodeConfig()));
        paramMap.put("image", serviceVersion.getImage());
        paramMap.put("model", JSONObject.parseObject(serviceVersion.getModel()));
        paramMap.put("service_type", service.getServiceType());
        String req = HttpUtils.sendPost(argoUrl + modelService + "/create", JSON.toJSONString(paramMap));
        if (StringUtils.isNotEmpty(req)) {
            Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req);
            if ((Integer) reqMap.get("code") == 200) {
                Map<String, String> data = (Map<String, String>) reqMap.get("data");
                serviceVersion.setUrl(data.get("url"));
                serviceVersion.setDeploymentName(data.get("deployment_name"));
                serviceVersion.setSvcName(data.get("svc_name"));
                serviceVersion.setRunState(Constant.Pending);
                serviceDao.updateServiceVersion(serviceVersion);
                return "启动成功";
            } else {
                throw new RuntimeException("启动失败");
            }
        } else {
            throw new RuntimeException("启动失败");
        }
    }

    @Override
    public String stopServiceVersion(Long id) {
        ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("deployment_name", serviceVersion.getDeploymentName());
        String req = HttpUtils.sendPost(argoUrl + modelService + "/stop", JSON.toJSONString(paramMap));
        if (StringUtils.isNotEmpty(req)) {
            serviceVersion.setRunState(Constant.Stopped);
            serviceDao.updateServiceVersion(serviceVersion);
            return "停止成功";
        } else {
            throw new RuntimeException("停止失败");
        }
    }

    @Override
    public String updateServiceVersion(ServiceVersion serviceVersion) {
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("deployment_name", serviceVersion.getDeploymentName());
        HashMap<String, Object> updateMap = new HashMap<>();
        updateMap.put("replicas", serviceVersion.getReplicas());
        updateMap.put("resource", serviceVersion.getResource());
        paramMap.put("update_model", new JSONObject(updateMap));
        String req = HttpUtils.sendPost(argoUrl + modelService + "/update", JSON.toJSONString(paramMap));
        if (StringUtils.isNotEmpty(req)) {
            return "修改成功";
        } else {
            throw new RuntimeException("更新失败");
        }
    }

    @Override
    public HashMap<String, String> getServiceVersionLog(Long id, String startTime, String endTime) {
        ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("deployment_name", serviceVersion.getDeploymentName());
        paramMap.put("start_time", startTime);
        paramMap.put("end_time", endTime);
        String req = HttpUtils.sendPost(argoUrl + modelService + "/getLog", JSON.toJSONString(paramMap));
        if (StringUtils.isNotEmpty(req)) {
            Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req);
            if (reqMap.get("code") != null && (Integer) reqMap.get("code") == 200) {
                return (HashMap<String, String>) reqMap.get("data");
            } else {
                throw new RuntimeException("获取日志失败");
            }
        } else {
            throw new RuntimeException("获取日志失败");
        }
    }

    @Override
    public Map<String, Object> getServiceVersionDocs(Long id) {
        ServiceVersion serviceVersion = serviceDao.getServiceVersionById(id);
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("deployment_name", serviceVersion.getDeploymentName());
        String req = HttpUtils.sendPost(argoUrl + modelService + "/getDocs", JSON.toJSONString(paramMap));
        if (StringUtils.isNotEmpty(req)) {
            Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req);
            Map<String, Object> data = (Map<String, Object>) reqMap.get("data");
            return data;
        } else {
            throw new RuntimeException("获取日志失败");
        }
    }

    @Override
    public List<ServiceVersion> serviceVersionList(Long id) {
        return serviceDao.getServiceVersionList(id);
    }


    ServiceVersion getServiceVersion(ServiceVersionVo serviceVersionVo) {
        ServiceVersion serviceVersion = new ServiceVersion();
        BeanUtils.copyProperties(serviceVersionVo, serviceVersion);
        serviceVersion.setModel(JSON.toJSONString(serviceVersionVo.getModel()));
        serviceVersion.setCodeConfig(JSON.toJSONString(serviceVersionVo.getCodeConfig()));
        serviceVersion.setEnvVariables(JSON.toJSONString(serviceVersionVo.getEnvVariables()));

        return serviceVersion;
    }

    List<ServiceVersionVo> getServiceVersionVoList(List<ServiceVersion> serviceVersionList) {
        List<ServiceVersionVo> result = new ArrayList<>();

        for (ServiceVersion sv : serviceVersionList) {
            ServiceVersionVo serviceVersionVo = getServiceVersionVo(sv);
            result.add(serviceVersionVo);
        }
        return result;
    }

    ServiceVersionVo getServiceVersionVo(ServiceVersion sv) {
        ServiceVersionVo serviceVersionVo = new ServiceVersionVo();
        BeanUtils.copyProperties(sv, serviceVersionVo);
        ServiceModelVo serviceModelVo = JSON.parseObject(sv.getModel(), ServiceModelVo.class);
        serviceVersionVo.setModel(serviceModelVo);

        ServiceCodeConfigVo serviceCodeConfigVo = JSON.parseObject(sv.getCodeConfig(), ServiceCodeConfigVo.class);
        serviceVersionVo.setCodeConfig(serviceCodeConfigVo);

        serviceVersionVo.setEnvVariables(JacksonUtil.parseJSONStr2Map(sv.getEnvVariables()));
        return serviceVersionVo;
    }

    Map<String, String> getRunState(List<String> deploymentNames) {
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("deployment_names", deploymentNames);
        String req = HttpUtils.sendPost(argoUrl + modelService + "/getStatus", JSON.toJSONString(paramMap));
        if (StringUtils.isNotEmpty(req)) {
            Map<String, Object> reqMap = JacksonUtil.parseJSONStr2Map(req);
            Map<String, Map<String, String>> data = (Map<String, Map<String, String>>) reqMap.get("data");
            if (data != null) {
                return data.get("status");
            }
            return null;
        } else {
            return null;
        }
    }

    void updateRunState(Map<String, String> runStates, List<ServiceVersion> serviceVersionList) {
        for (ServiceVersion sv : serviceVersionList) {
            String runState = runStates.get(sv.getDeploymentName());
            sv.setRunState(runState);
            serviceDao.updateRunState(sv.getDeploymentName(), runState);
        }
    }
}
