package com.ruoyi.platform.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.util.DateUtils;
import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.domain.*;
import com.ruoyi.platform.mapper.*;
import com.ruoyi.platform.service.ImageService;
import com.ruoyi.platform.service.ImageVersionService;
import com.ruoyi.platform.service.MinioService;
import com.ruoyi.platform.utils.DockerClientUtil;
import com.ruoyi.platform.utils.FileUtil;
import com.ruoyi.platform.utils.JacksonUtil;
import com.ruoyi.platform.utils.K8sClientUtil;
import com.ruoyi.platform.vo.ImageVo;
import com.ruoyi.system.api.constant.Constant;
import com.ruoyi.system.api.model.LoginUser;
import io.kubernetes.client.openapi.models.V1Pod;
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.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * (Image)表服务实现类
 *
 * @author Xidaray
 * @since 2024-03-05 14:46:58
 */
@Service("imageService")
public class ImageServiceImpl implements ImageService {
    @Resource
    private ImageDao imageDao;
    @Resource
    private ImageVersionDao imageVersionDao;
    @Resource
    private DevEnvironmentDao devEnvironmentDao;
    @Resource
    private AssetWorkflowDao assetWorkflowDao;
    @Resource
    private RayDao rayDao;
    @Resource
    private ActiveLearnDao activeLearnDao;
    @Resource
    private ImageVersionService imageVersionService;
    @Resource
    private ServiceDao serviceDao;
    @Resource
    private K8sClientUtil k8sClientUtil;
    @Resource
    private DockerClientUtil dockerClientUtil;

    @Resource
    private MinioService minioService;
    @Value("${minio.dataReleaseBucketName}")
    private String bucketName;
    //    @Value("${harbor.bucketName}")
//    private String bucketName;
    @Value("${harbor.repository}")
    private String repository;
    @Value("${harbor.harborUrl}")
    private String harborUrl;
    @Value("${harbor.harborUser}")
    private String harborUser;
    @Value("${harbor.harborpassword}")
    private String harborpassword;
    @Value("${harbor.deploymentName}")
    private String deploymentName;
    @Value("${harbor.serviceNS}")
    private String serviceNS;

    @Value("${dockerpush.image}")
    private String image;
    @Value("${dockerpush.mountPath}")
    private String mountPath;
    @Value("${jupyter.namespace}")
    private String namespace;

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

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


    /**
     * 新增数据
     *
     * @param image 实例对象
     * @return 实例对象
     */
    @Override
    public Image insert(Image image) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        image.setCreateBy(loginUser.getUsername());
        image.setUpdateBy(loginUser.getUsername());
        image.setUpdateTime(new Date());
        image.setCreateTime(new Date());
        image.setState(1);
        this.imageDao.insert(image);
        return image;
    }

    /**
     * 修改数据
     *
     * @param image 实例对象
     * @return 实例对象
     */
    @Override
    public Image update(Image image) {
        int currentState = image.getState();
        if (currentState == 0) {
            throw new RuntimeException("镜像已被删除，无法更新");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        image.setUpdateBy(loginUser.getUsername());
        image.setUpdateTime(new Date());
        this.imageDao.update(image);
        return this.queryById(image.getId());

    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Integer id) {
        return this.imageDao.deleteById(id) > 0;
    }

    @Override
    public String removeById(Integer id) throws Exception {
        Image image = this.imageDao.queryById(id);
        if (image == null) {
            throw new Exception("镜像不存在");
        }

        List<AssetWorkflow> assetWorkflow = assetWorkflowDao.getAssetWorkflow(Long.valueOf(id), Constant.Asset_Type_Image, null);
        if (assetWorkflow != null && !assetWorkflow.isEmpty()) {
            String workflows = String.join(",", assetWorkflow.stream().map(AssetWorkflow::getWorkflowName).collect(Collectors.toSet()));
            throw new Exception("该镜像被流水线：" + workflows + "使用，不能删除，请先删除流水线");
        }

        HashMap<String, String> map = new HashMap<>();
        map.put("id", String.valueOf(id));
        List<Ray> rayList = rayDao.queryByImageId(JSON.toJSONString(map));
        if (rayList != null && !rayList.isEmpty()) {
            String rays = String.join(",", rayList.stream().map(Ray::getName).collect(Collectors.toSet()));
            throw new Exception("该镜像被超参数自动寻优：" + rays + "使用，不能删除，请先删除超参数自动寻优");
        }

        List<ActiveLearn> activeLearnList = activeLearnDao.queryByImageId(JSON.toJSONString(map));
        if (activeLearnList != null && !activeLearnList.isEmpty()) {
            String activeLearns = String.join(",", activeLearnList.stream().map(ActiveLearn::getName).collect(Collectors.toSet()));
            throw new Exception("该镜像被主动学习：" + activeLearns + "使用，不能删除，请先删除主动学习");
        }

        List<DevEnvironment> devEnvironmentList = devEnvironmentDao.queryByImageId(JSON.toJSONString(map));
        if (devEnvironmentList != null && !devEnvironmentList.isEmpty()) {
            String devEnvironments = String.join(",", devEnvironmentList.stream().map(DevEnvironment::getName).collect(Collectors.toSet()));
            throw new Exception("该镜像被开发环境：" + devEnvironments + "使用，不能删除，请先删除开发环境");
        }

        List<String> serviceVersionList = serviceDao.queryByImageId(JSON.toJSONString(map));
        if (serviceVersionList != null && !serviceVersionList.isEmpty()) {
            String serviceVersions = String.join(",", serviceVersionList.stream().collect(Collectors.toSet()));
            throw new Exception("该镜像被服务版本：" + serviceVersions + "使用，不能删除，请先删除服务版本");
        }

        //判断权限，只有admin和创建者本身可以删除该数据集
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String username = loginUser.getUsername();

        String createdBy = image.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) {
            throw new Exception("无权限删除该镜像");
        }

        List<ImageVersion> imageVersions = imageVersionService.queryByImageId(id);

        for (ImageVersion imageVersion : imageVersions) {
            if (StringUtils.isNotEmpty(imageVersion.getHostIp())) {
                dockerClientUtil.removeImage(imageVersion.getUrl(), imageVersion.getHostIp());
            }
        }

        image.setState(Constant.State_invalid);
        return this.imageDao.update(image) > 0 ? "删除成功" : "删除失败";


    }

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


    /**
     * 通过名字精确查询镜像
     *
     * @param name 名字
     * @return 镜像
     */
    @Override
    public Image getByName(String name) {
        return this.imageDao.getByName(name);
    }

    @Override
    @Transactional
    public GenericsAjaxResult<String> insertImageAndVersion(ImageVo imageVo) throws Exception {
        Image existingImage = getByName(imageVo.getName());
        Image imageToUse;
        if (existingImage == null) {
            // 如果不存在相同名称的镜像，则创建新的镜像记录
            Image newImage = new Image();
            newImage.setName(imageVo.getName());
            newImage.setDescription(imageVo.getDescription());
            newImage.setImageType(imageVo.getImageType());
            imageToUse = this.insert(newImage);
            if (imageToUse == null) {
                throw new Exception("新增镜像失败");
            }
        } else {
            // 如果已存在相同名称的镜像，使用已存在的镜像
            imageToUse = existingImage;
            ImageVersion query = new ImageVersion();
            query.setImageId(imageToUse.getId());
            query.setTagName(imageVo.getTagName());
            long count = imageVersionDao.count(query);
            if (count != 0) {
                return GenericsAjaxResult.error("镜像版本已存在");
            }
        }
        ImageVersion imageVersion = new ImageVersion();
        imageVersion.setImageId(imageToUse.getId());
        imageVersion.setVersion(imageVo.getVersion());
        imageVersion.setTagName(imageVo.getTagName());
        imageVersion.setFileSize(imageVo.getFileSize());
        imageVersion.setDescription(imageVo.getVersionDescription());
        imageVersion.setStatus(Constant.Building);
        ImageVersion imageVersionInsert = this.imageVersionService.insert(imageVersion);
        if (imageVersionInsert == null) {
            throw new Exception("新增镜像版本失败");
        }
        // 使用CompletableFuture异步执行不同的镜像构建逻辑，在构建镜像的同时，更新数据库中的镜像版本状态
        CompletableFuture.supplyAsync(() -> {
            Map<String, String> resultMap = new HashMap<>();
            try {
                if (imageVo.getUploadType() == 0) {
                    resultMap = createImageFromNet(imageVo.getName(), imageVo.getTagName(), imageVo.getPath());
                } else {
                    resultMap = createImageFromLocal(imageVo.getName(), imageVo.getTagName(), imageVo.getFileName());
                }
            } catch (Exception e) {
                imageVersion.setStatus(Constant.Failed);
                imageVersionService.update(imageVersion);
                throw new RuntimeException("镜像构建失败: " + e.getMessage(), e);
            }
            return resultMap;
        }).thenAccept(resultMap -> {
            try {
                String imageUrl = resultMap.get("url");
                String fileSize = resultMap.get("fileSize");
                imageVersion.setUrl(imageUrl);
                imageVersion.setFileSize(fileSize);
                imageVersion.setStatus(Constant.Available);
                imageVersionService.update(imageVersion);
            } catch (Exception e) {
                System.err.println("更新数据库失败: " + e.getMessage());
            }
        }).exceptionally(ex -> {
            System.err.println("异步操作发生错误: " + ex.getMessage());
            return null;
        });

        return GenericsAjaxResult.success("新增镜像成功,镜像构建中...");
    }

    @Override
    public Map<String, String> createImageFromNet(String imageName, String imageTag, String netPath) throws Exception {
        Map<String, String> resultMap = new HashMap<>();
        // 得到容器
        V1Pod pod = k8sClientUtil.getNSPodList(serviceNS, deploymentName);
        if (pod == null) {
            String podName = deploymentName + "-" + DateUtils.formatYMD10(new Date());
            pod = k8sClientUtil.createPodWithEnv(podName, serviceNS, mountPath, image);
        }
        String loginCmd = "docker login -u " + harborUser + " -p " + harborpassword + " " + harborUrl;
        //  执行命令 docker login -u admin -p Harbor12345 172.20.32.187
        String loginlog = k8sClientUtil.executeCommand(pod, loginCmd);
        //   在这个容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag
        String username = SecurityUtils.getLoginUser().getUsername();
        //
        String logs2 = k8sClientUtil.executeCommand(pod, "docker pull " + netPath);
        //   在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag
        if (StringUtils.isNoneBlank(logs2)) {
            String[] lines = logs2.split("\n");
            String lastLine = lines[lines.length - 1].trim();
            String tagCmd = "docker tag " + lastLine + " " + harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
            String imageUrl = harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
            String pushCmd = "docker push " + imageUrl;
            String sizeCmd = "docker inspect --format='{{.Size}}' " + imageUrl;
            String s = k8sClientUtil.executeCommand(pod, tagCmd);

            if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, pushCmd))) {
                resultMap.put("url", imageUrl);
                //得到镜像文件大小
                String imageSizeStr = k8sClientUtil.executeCommand(pod, sizeCmd);
                long sizeInBytes = Long.parseLong(imageSizeStr.trim());
                String formattedImageSize = FileUtil.formatFileSize(sizeInBytes); // 格式化镜像文件大小
                resultMap.put("fileSize", formattedImageSize);
                return resultMap;

            } else {
                throw new Exception("拉取公网镜像失败，请检查网络或者镜像地址");
            }
        } else {
            throw new Exception("拉取公网镜像失败，请检查网络或者镜像地址");
        }
    }

    @Override
    public Map<String, String> createImageFromLocal(String imageName, String imageTag, String fileName) throws Exception {
        Map<String, String> resultMap = new HashMap<>();
        // 得到容器
        V1Pod pod = k8sClientUtil.getNSPodList(serviceNS, deploymentName);
        if (pod == null) {
            String podName = deploymentName + "-" + DateUtils.formatYMD10(new Date());
            pod = k8sClientUtil.createPodWithEnv(podName, serviceNS, mountPath, image);
        }
        String loginCmd = "docker login -u " + harborUser + " -p " + harborpassword + " " + harborUrl;
        //  执行命令 docker login -u admin -p Harbor12345 172.20.32.187
        String loginlog = k8sClientUtil.executeCommand(pod, loginCmd);
        //   在这个容器的/data/admin 目录下执行命令 docker load -i fileName 得到返回的镜像名字name:tag
        String username = SecurityUtils.getLoginUser().getUsername();
        //
        String filePath = mountPath + "/" + username + "/" + fileName;
        String logs2 = k8sClientUtil.executeCommand(pod, "docker load -i " + filePath);
        //   在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag

        if (StringUtils.isNoneBlank(logs2)) {
            String substring = logs2.substring(logs2.indexOf(":") + 1).trim();
            String tagCmd = "docker tag " + substring + " " + harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
            String imageUrl = harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
            String pushCmd = "docker push " + imageUrl;
            String sizeCmd = "docker inspect --format='{{.Size}}' " + imageUrl;
            String s = k8sClientUtil.executeCommand(pod, tagCmd);
            if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, pushCmd))) {
                resultMap.put("url", imageUrl);
                //得到镜像文件大小
                String imageSizeStr = k8sClientUtil.executeCommand(pod, sizeCmd);
                long sizeInBytes = Long.parseLong(imageSizeStr.trim());
                String formattedImageSize = FileUtil.formatFileSize(sizeInBytes); // 格式化镜像文件大小
                resultMap.put("fileSize", formattedImageSize);
                return resultMap;
            } else {
                throw new Exception("解析镜像压缩包失败，请检查镜像文件");
            }
        } else {
            throw new Exception("解析镜像压缩包失败，请检查镜像文件");
        }
    }


    @Override
    public Map<String, String> uploadImageFiles(MultipartFile file) throws Exception {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String path = "/mini-model-platform-data/images/" + loginUser.getUsername() + "/" + file.getOriginalFilename();
        return minioService.uploadFile(bucketName, path, file);
    }

    @Override
    @Transactional
    @Async
    public void saveImage(ImageVo imageVo) {
        Image oldImage = imageDao.getByName(imageVo.getName());
        if (oldImage != null) {
            List<ImageVersion> oldImageVersions = imageVersionDao.queryByImageId(oldImage.getId());
            for (ImageVersion oldImageVersion : oldImageVersions) {
                if (oldImageVersion.getTagName().equals(imageVo.getTagName())) {
                    throw new IllegalStateException("镜像tag不能重复");
                }
            }
        }

        LoginUser loginUser = SecurityUtils.getLoginUser();
        String username = loginUser.getUsername().toLowerCase();
        String podName = username + "-editor-pod" + "-" + imageVo.getDevEnvironmentId().toString();

        try {
            String containerId = k8sClientUtil.getPodContainerId(podName, namespace);
            String hostIp = k8sClientUtil.getHostIp(podName, namespace);

            dockerClientUtil.commitImage(imageVo, containerId, hostIp, username);
            HashMap<String, String> resultMap = dockerClientUtil.pushImageToHorbor(imageVo, hostIp);

            Image image = new Image();
            BeanUtils.copyProperties(imageVo, image);
            image.setImageType(Constant.Image_Type_Pri);
            image.setCreateBy(username);
            image.setUpdateBy(username);
            image.setUpdateTime(new Date());
            image.setCreateTime(new Date());
            image.setState(Constant.State_valid);
            imageDao.insert(image);

            ImageVersion imageVersion = new ImageVersion();
            imageVersion.setImageId(image.getId());
            imageVersion.setVersion(imageVo.getVersion());
            imageVersion.setUrl(resultMap.get("imageName"));
            imageVersion.setTagName(imageVo.getTagName());
            imageVersion.setFileSize(resultMap.get("size"));
            imageVersion.setCreateBy(username);
            imageVersion.setUpdateBy(username);
            imageVersion.setHostIp(hostIp);
            imageVersion.setUpdateTime(new Date());
            imageVersion.setCreateTime(new Date());
            imageVersion.setState(Constant.State_valid);
            imageVersion.setStatus(Constant.Available);
            imageVersionDao.insert(imageVersion);

            //更新dev环境的镜像信息
            DevEnvironment devEnvironment = new DevEnvironment();
            devEnvironment.setId(imageVo.getDevEnvironmentId());


            imageVo.setValue(resultMap.get("imageName"));
            imageVo.setVersion(String.valueOf(imageVersion.getId()));

            resultMap.put("id", String.valueOf(image.getId()));
            resultMap.put("version", String.valueOf(imageVersion.getId()));
            resultMap.put("value",resultMap.get("imageName"));

            devEnvironment.setImage(JacksonUtil.toJSONString(resultMap));
            devEnvironmentDao.update(devEnvironment);
        } catch (Exception e) {
            throw new RuntimeException("保存镜像失败:" + e);
        }
    }
}
