package com.ruoyi.platform.service.impl;

import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.platform.annotations.CheckDuplicate;
import com.ruoyi.platform.domain.AssetIcon;
import com.ruoyi.platform.domain.Dataset;
import com.ruoyi.platform.domain.Models;
import com.ruoyi.platform.domain.ModelsVersion;
import com.ruoyi.platform.mapper.ModelsDao;
import com.ruoyi.platform.mapper.ModelsVersionDao;
import com.ruoyi.platform.service.AssetIconService;
import com.ruoyi.platform.service.MinioService;
import com.ruoyi.platform.service.ModelsService;
import com.ruoyi.platform.service.ModelsVersionService;
import com.ruoyi.platform.utils.BeansUtils;
import com.ruoyi.platform.utils.FileUtil;
import com.ruoyi.platform.utils.MinioUtil;
import com.ruoyi.platform.vo.ModelsVo;
import com.ruoyi.platform.vo.VersionVo;
import com.ruoyi.system.api.model.LoginUser;
import io.minio.messages.Item;
import io.netty.util.Version;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * (Models)表服务实现类
 *
 * @author Xidaray
 * @since 2023-11-28 16:26:07
 */
@Service("modelsService")
public class ModelsServiceImpl implements ModelsService {
    @Resource
    private ModelsDao modelsDao;
    @Resource
    private ModelsVersionDao modelsVersionDao;

    @Resource
    private ModelsVersionService modelsVersionService;

    @Resource
    private MinioService minioService;

    @Resource
    private AssetIconService assetIconService;


    // 固定存储桶名
    @Value("${minio.dataReleaseBucketName}")
    private String bucketName;
    @Resource
    private  MinioUtil minioUtil;


    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public Models queryById(Integer id) {
        Models models = this.modelsDao.queryById(id);
        String modelType = models.getModelType();
        String modelTag = models.getModelTag();
        //去资产管理表中查询对应的图标名,注意判空逻辑，只有当dataType和dataTag不为空时，才进行查询
        if(modelType != null && !modelType.isEmpty()){
            AssetIcon modelTypeAssetIcon = assetIconService.queryById(Integer.valueOf(modelType));
            if (modelTypeAssetIcon != null){
                models.setModelTypeName(modelTypeAssetIcon.getName());
            }
        }
        if(modelTag != null && !modelTag.isEmpty()){
            AssetIcon modelTagAssetIcon = assetIconService.queryById(Integer.valueOf(modelTag));
            if (modelTagAssetIcon != null){
                models.setModelTagName(modelTagAssetIcon.getName());
            }
        }
        return models;
    }

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

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

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

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

    @Override
    public String removeById(Integer id) throws Exception {
        Models models = this.modelsDao.queryById(id);
        if (models == null){
            throw new Exception("模型不存在");
        }

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


        String createdBy = models.getCreateBy();
        if (!(StringUtils.equals(username,"admin") || StringUtils.equals(username,createdBy))){
            throw new Exception("无权限删除该模型");
        }
        //判断是否有版本文件
        if (!modelsVersionService.queryByModelsId(id).isEmpty()){
            throw new Exception("请先删除该模型下的版本文件");
        }
        models.setState(0);
        return this.modelsDao.update(models)>0?"删除成功":"删除失败";
    }

    /**
     * 下载模型
     *
     * @param id models_version表的主键
     * @return 文件内容
     *
     */

    @Override
    public ResponseEntity<InputStreamResource> downloadModels(Integer id) throws Exception {
        ModelsVersion modelsVersion = this.modelsVersionDao.queryById(id);
        if (modelsVersion == null) {
            throw new Exception("未找到该模型下版本记录");

        }
        // 从数据库中获取存储路径（即MinIO中的对象名称）
        String objectName = modelsVersion.getUrl();
        if(objectName == null || objectName.isEmpty() ){
            throw new Exception("未找到该版本模型文件");
        }

        try {
            // 使用ByteArrayOutputStream来捕获下载的数据
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            minioUtil.downloadObject(bucketName,objectName,outputStream);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            InputStreamResource resource = new InputStreamResource(inputStream);

            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(extractFileName(objectName),"UTF-8") + "\"")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);


        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("下载模型文件错误");
        }
    }

    /**
     * 上传模型文件
     *
     * @param files 文件
     * @param uuid 唯一标识
     * @return 是否成功
     */
    @Override
    public List<Map<String, String>> uploadModels(MultipartFile[] files, String uuid) throws Exception {
        List<Map<String, String>> results = new ArrayList<>();
        for (MultipartFile file:files) {
            // 构建objectName
            String username = SecurityUtils.getLoginUser().getUsername();
            String fileName = file.getOriginalFilename();
            String objectName = "models/" + username + "/" + uuid + "/" + fileName;
            results.add(minioService.uploadFile(bucketName, objectName, file));
        }
        return results;
    }


    @Override
    public Map uploadModelsPipeline(ModelsVersion modelsVersion) throws Exception {
        String username = SecurityUtils.getLoginUser().getUsername();
        // 检查是否存在
        Models models = this.queryById(modelsVersion.getModelsId());

        if (models == null) {
            throw new Exception("模型不存在，请检查模型id");
        }

        ModelsVersion version = modelsVersionService.queryByModelsVersion(modelsVersion);

        String url = "";
        if (version == null) {
            //插表,因为这里是一次直接插表所以这里定掉date，然后用DAO插入
            Date date = new Date();
            //String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(date);
            url = "models/" + username + "/" + models.getName() + "-" +  "/" + modelsVersion.getVersion() + "/" + modelsVersion.getFileName();
            modelsVersion.setUrl(url);
            modelsVersion.setCreateBy(username);
            modelsVersion.setUpdateBy(username);
            modelsVersion.setCreateTime(date);
            modelsVersion.setUpdateTime(date);
            modelsVersion.setState(1);
            modelsVersionDao.insert(modelsVersion);
        }else {
            //改表
            BeansUtils.copyPropertiesIgnoreNull(modelsVersion, version);
            Date createTime = version.getCreateTime();
            // String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(createTime);
            url = "models/" + username + "/" + models.getName() + "-" + "/" + modelsVersion.getVersion() + "/" + modelsVersion.getFileName();
            version.setUrl(url);
            modelsVersionService.update(version);
        }
        Map<String, String> result = new HashMap<String, String>();
        result.put("url",url);
        return result;
    }

    /**
     * 下载所有模型文件，用压缩包的方式返回
     *
     * @param modelsId  模型ID
     * @param version   模型版本号
     * @return 文件内容
     */
    @Override
    public ResponseEntity<InputStreamResource> downloadAllModelFiles(Integer modelsId, String version) throws Exception {
        // 根据模型id查模型名
        Models model  = this.modelsDao.queryById(modelsId);
        String modelName = model.getName();
        // 查询特定模型和版本对应的所有文件
        List<ModelsVersion> modelsVersionList = this.modelsVersionDao.queryAllByModelsVersion(modelsId, version);
        if (modelsVersionList == null || modelsVersionList.isEmpty()) {
            throw new Exception("模型文件记录不存在，请检查模型id");
        }

        // 创建ZIP文件，准备打包下载
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(baos)) {
            //遍历每一个version数据项
            for (ModelsVersion modelsVersion : modelsVersionList) {
                String objectName = modelsVersion.getUrl();
                if (objectName != null && !objectName.isEmpty()) {
                    // 使用ByteArrayOutputStream来捕获MinIO中的文件
                    ByteArrayOutputStream fileOutputStream = new ByteArrayOutputStream();
                    minioUtil.downloadObject(bucketName, objectName, fileOutputStream);
                    // 添加到ZIP文件
                    zos.putNextEntry(new ZipEntry(extractFileName(objectName)));
                    fileOutputStream.writeTo(zos);
                    zos.closeEntry();
                }
            }

            // 转换为输入流
            ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
            InputStreamResource resource = new InputStreamResource(inputStream);

            // 设置响应
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + modelName + "_"  + version + ".zip\"")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("模型文件打包下载失败");
        }

    }

    @Override
    public List<String> getModelVersions(Integer modelId) throws Exception {
        // 获取所有相同模型ID的记录
        List<ModelsVersion> versions = modelsVersionDao.queryByModelsId(modelId);

        if (versions.isEmpty()) {
            return new ArrayList<>(); // 如果没有找到任何版本记录，返回空列表
        }

        // 使用Stream API提取version字段，并去重
        return versions.stream()
                .map(ModelsVersion::getVersion) // 提取每个ModelVersion对象的version属性
                .filter(version -> version != null && !version.isEmpty()) //忽略null或空字符串的version
                .distinct() // 去重
                .collect(Collectors.toList()); // 收集到List中
    }


    @Override
    @Transactional
    public String insertModelAndVersion(ModelsVo modelsVo) throws Exception {
        List<VersionVo> modelsVersionVos = modelsVo.getModelsVersionVos();

        if (modelsVersionVos==null || modelsVersionVos.isEmpty()){
            throw new Exception("模型版本信息错误");
        }
        Models models = new Models();
        models.setName(modelsVo.getName());
        models.setDescription(modelsVo.getDescription());
        models.setAvailableRange(modelsVo.getAvailableRange());
        models.setModelType(modelsVo.getModelType());
        models.setModelTag(modelsVo.getModelTag());
        Models modelsInsert = this.insert(models);
        if (modelsInsert == null){
            throw new Exception("新增模型失败");
        }
        //遍历版本信息列表，把文件信息插入数据库
        for(VersionVo modelsVersionVo : modelsVersionVos){
            ModelsVersion modelsVersion = new ModelsVersion();
            modelsVersion.setModelsId(modelsInsert.getId());
            modelsVersion.setVersion(modelsVo.getVersion());
            modelsVersion.setUrl(modelsVersionVo.getUrl());
            modelsVersion.setFileName(modelsVersionVo.getFileName());
            modelsVersion.setFileSize(modelsVersionVo.getFileSize());
            ModelsVersion modelsVersionInsert = this.modelsVersionService.insert(modelsVersion);
            if (modelsVersionInsert == null) {
                throw new Exception("新增模型版本失败");
            }
        }

        return "新增模型成功";

    }


    /**
     *  根据模型id和版本读取文件内容
     *
     * @param modelsId  模型ID
     * @param version   模型版本号
     * @return 文件内容
     */
    @Override
    public String readFileContent(Integer modelsId, String version) throws Exception {
        // 根据模型id查模型名
        Models model  = this.modelsDao.queryById(modelsId);
        if (model == null) {
            throw new Exception("模型不存在");
        }
        String modelName = model.getName();
        String objectName = "";
        // 查询特定模型和版本对应的所有文件
        List<ModelsVersion> modelsVersionList = this.modelsVersionDao.queryAllByModelsVersion(modelsId, version);
        if (modelsVersionList == null || modelsVersionList.isEmpty()) {
            throw new Exception("对应模型版本不存在");
        }
        //遍历文件列表
        for(ModelsVersion modelsVersion : modelsVersionList){
            String fileName = modelsVersion.getFileName();
            if("readme.md".equalsIgnoreCase(fileName)){
                //如果存在readme文件，读取minio中的url
                objectName = modelsVersion.getUrl();
                // 读取MinIO中的对象为字符串
                try {
                    return minioUtil.readObjectAsString(bucketName, objectName);
                } catch (Exception e) {
                    throw new Exception("读取README.md文件失败", e);
                }
            }
        }

        // 如果没有找到README.md文件，返回null或者抛出一个异常
        throw new Exception("未找到README.md文件");


    }

    @Override
    public void checkDeclaredName(Models insert) throws Exception {
        Models existingModel = modelsDao.findByName(insert.getName());
        if (existingModel != null) {
            // Check if the found models is not the same as the one being inserted
            // This is important if you are using this method for both insert and update operations
            // You may need an identifier check here, e.g., if 'insert' has an ID and it's the same as 'existingDataset'
            if (insert.getId() != null && insert.getId().equals(existingModel.getId())) {
                // This is the same dataset, no duplicate name issue for update operation
                return;
            }

            // Now we know there's another dataset with the same name
            Field[] fields = Models.class.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true); // Make private fields accessible
                if ("name".equals(field.getName()) && field.isAnnotationPresent(CheckDuplicate.class)) {
                    // If the field is 'name' and is marked with CheckDuplicate annotation
                    CheckDuplicate annotation = field.getAnnotation(CheckDuplicate.class);
                    throw new Exception("重复的模型名称: " + insert.getName() + ". " + annotation.message());
                }
            }
        }
    }

    @Override
    public List<Map<String, String>> exportModels(String path, String uuid) throws Exception {
        List<Map<String, String>> results = new ArrayList<>();
        //根据path得到源文件所在桶名
        String srcBucketName = path.substring(0, path.indexOf("/"));
        //构建目标目录路径
        String username = SecurityUtils.getLoginUser().getUsername();
        String srcDir = path.substring(path.indexOf("/") + 1);
        String targetDir = "models/" + username + "/" + uuid + "/";
        // 递归拷贝整个原目录到目标目录下
        minioUtil.copyDirectory(srcBucketName, srcDir, bucketName, targetDir);
        List<Item> movedItems = minioUtil.getAllObjectsByPrefix(bucketName, targetDir, true);
        for (Item movedItem : movedItems) {
            if(!movedItem.isDir() && movedItem.size() > 0){ // 检查是否为非目录且文件大小大于0
                Map<String, String> result = new HashMap<>();
                String url = movedItem.objectName();
                String fileName = extractFileName(url);
                // 获取文件大小并转换为可读形式
                long sizeInBytes = movedItem.size();
                String formattedSize = FileUtil.formatFileSize(sizeInBytes); // 格式化文件大小
                result.put("fileName", fileName);
                result.put("url", url); // objectName根据实际情况定义
                result.put("fileSize", formattedSize);
                results.add(result);
            }
        }
        return results;
    }

    private String extractFileName(String urlStr) {
        return urlStr.substring(urlStr.lastIndexOf('/') + 1);
    }


}
