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.DatasetVersion;
import com.ruoyi.platform.mapper.DatasetDao;
import com.ruoyi.platform.mapper.DatasetVersionDao;
import com.ruoyi.platform.service.*;
import com.ruoyi.platform.utils.BeansUtils;
import com.ruoyi.platform.utils.FileUtil;
import com.ruoyi.platform.utils.MinioUtil;
import com.ruoyi.platform.vo.DatasetVo;
import com.ruoyi.platform.vo.VersionVo;
import com.ruoyi.system.api.model.LoginUser;
import io.minio.messages.Item;
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.HttpStatus;
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.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * (Dataset)表服务实现类
 *
 * @author Xidaray
 * @since 2023-11-28 11:51:22
 */
@Service("datasetService")
public class DatasetServiceImpl implements DatasetService {
    @Resource
    private DatasetDao datasetDao;

    @Resource
    private DatasetVersionDao datasetVersionDao;

    @Resource
    private DatasetVersionService datasetVersionService;

    @Resource
    private AssetIconService assetIconService;

    @Resource
    private MinioService minioService;

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

    @Resource
    private MinioUtil minioUtil;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public Dataset queryById(Integer id) {
        Dataset dataset = this.datasetDao.queryById(id);
        if (dataset != null) {
            String dataType = dataset.getDataType();
            String dataTag = dataset.getDataTag();

            // 判空逻辑，只有当dataType和dataTag不为空时，才进行查询
            if (dataType != null && !dataType.isEmpty()) {
                AssetIcon dataTypeAssetIcon = assetIconService.queryById(Integer.valueOf(dataType));
                if (dataTypeAssetIcon != null) {
                    dataset.setDatasetTypeName(dataTypeAssetIcon.getName());

                }
            }
            if (dataTag != null && !dataTag.isEmpty()) {
                AssetIcon dataTagAssetIcon = assetIconService.queryById(Integer.valueOf(dataTag));
                if (dataTagAssetIcon != null) {
                    dataset.setDatasetTagName(dataTagAssetIcon.getName());
                }
            }
        }
        return dataset;


    }

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

    }

    /**
     * 新增数据
     *
     * @param dataset 实例对象
     * @return 实例对象
     */
    @Override
    public Dataset insert(Dataset dataset) throws Exception {

        checkDeclaredName(dataset);

        LoginUser loginUser = SecurityUtils.getLoginUser();
        dataset.setCreateBy(loginUser.getUsername());
        dataset.setUpdateBy(loginUser.getUsername());
        dataset.setUpdateTime(new Date());
        dataset.setCreateTime(new Date());
        dataset.setState(1);
        this.datasetDao.insert(dataset);
        return dataset;
    }

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

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

    @Override
    public String removeById(Integer id) throws Exception {
        Dataset dataset = this.datasetDao.queryById(id);
        if (dataset == null) {
            throw new Exception("数据集不存在");
        }

        //判断权限，只有admin和创建者本身可以删除该数据集
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String username = loginUser.getUsername();
        String createdBy = dataset.getCreateBy();
        if (!(StringUtils.equals(username, "admin") || StringUtils.equals(username, createdBy))) {
            throw new Exception("无权限删除该数据集");
        }
        if (datasetVersionService.queryByDatasetId(id).size()>0){
            throw new Exception("请先删除该数据集下的版本文件");
        }

        dataset.setState(0);
        return this.datasetDao.update(dataset)>0?"删除数据集成功":"删除数据集失败";
    }

    /**
     * 下载数据集
     *
     * @param id 主键
     * @return 文件内容
     */

    @Override
    public ResponseEntity<InputStreamResource> downloadDataset(Integer id) throws Exception {
        DatasetVersion datasetVersion = this.datasetVersionDao.queryById(id);
        if (datasetVersion == null) {
            throw new Exception("未找到该版本下数据集");
        }

        // 从数据库中获取存储路径（即MinIO中的对象名称）
        String objectName = datasetVersion.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=\"" + extractFileName(objectName) + "\"")
                    .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>> uploadDataset(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 = "datasets/" + username + "/" + uuid + "/" + fileName;
            results.add(minioService.uploadFile(bucketName, objectName, file));
        }
        return results;
    }


    @Override
    public Map uploadDatasetPipeline(DatasetVersion datasetVersion) throws Exception {
        String username = SecurityUtils.getLoginUser().getUsername();
        // 检查是否存在
        Dataset dataset = this.queryById(datasetVersion.getDatasetId());
        if (dataset == null) {
            throw new Exception("数据集不存在，请检查数据集id");
        }
        DatasetVersion version = datasetVersionService.queryByDatasetVersion(datasetVersion);
        String url = "";
        if (version == null) {
            //插表,因为这里是一次直接插表所以这里定掉date，然后用DAO插入
            Date date = new Date();
            String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(date);
            url = "datasets/" + username + "/" + dataset.getName() + "-" + timestamp + "/" + datasetVersion.getVersion() + "/" + datasetVersion.getFileName();
            datasetVersion.setUrl(url);
            datasetVersion.setCreateBy(username);
            datasetVersion.setUpdateBy(username);
            datasetVersion.setCreateTime(date);
            datasetVersion.setUpdateTime(date);
            datasetVersion.setState(1);
            datasetVersionDao.insert(datasetVersion);
        }else {
            //改表
            BeansUtils.copyPropertiesIgnoreNull(datasetVersion, version);
            Date createTime = version.getCreateTime();
            String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(createTime);
            url = "datasets/" + username + "/" + dataset.getName() + "-" + timestamp + "/" + datasetVersion.getVersion() + "/" + datasetVersion.getFileName();
            version.setUrl(url);
            datasetVersionService.update(version);
        }
        Map<String, String> result = new HashMap<String, String>();
        result.put("url",url);
        return result;
    }

    @Override
    public List<String> getDatasetVersions(Integer datasetId) throws Exception {
        // 获取所有相同模型ID的记录
        List<DatasetVersion> versions = datasetVersionDao.queryByDatasetId(datasetId);
        if (versions.isEmpty()) {
            return new ArrayList<>(); // 如果没有找到任何版本记录，返回空列表
        }
        // 使用Stream API提取version字段，并去重
        return versions.stream()
                .map(DatasetVersion::getVersion) // 提取每个DatasetVersion对象的version属性
                .filter(version -> version != null && !version.isEmpty()) //忽略null或空字符串的version
                .distinct() // 去重
                .collect(Collectors.toList()); // 收集到List中
    }


    @Override
    @Transactional
    public String insertDatasetAndVersion(DatasetVo datasetVo) throws Exception {
        List<VersionVo> datasetVersionVos = datasetVo.getDatasetVersionVos();
        if (datasetVersionVos==null || datasetVersionVos.isEmpty()){
            throw new Exception("数据集版本信息错误");

        }

        Dataset dataset = new Dataset();
        dataset.setName(datasetVo.getName());
        dataset.setDescription(datasetVo.getDescription());
        dataset.setAvailableRange(datasetVo.getAvailableRange());
        dataset.setDataType(datasetVo.getDataType());
        dataset.setDataTag(datasetVo.getDataTag());
        Dataset datasetInsert = this.insert(dataset);
        if (datasetInsert == null){
            throw new Exception("新增数据集失败");
        }

        for (VersionVo datasetVersionVo : datasetVersionVos){
            DatasetVersion datasetVersion = new DatasetVersion();
            datasetVersion.setDatasetId(datasetInsert.getId());
            datasetVersion.setVersion(datasetVo.getVersion());
            datasetVersion.setUrl(datasetVersionVo.getUrl());
            datasetVersion.setFileName(datasetVersionVo.getFileName());
            datasetVersion.setFileSize(datasetVersionVo.getFileSize());
            datasetVersion.setAvailableCluster(datasetVo.getAvailableCluster());
            DatasetVersion datasetVersionInsert = datasetVersionService.insert(datasetVersion);
            if (datasetVersionInsert == null) {
                throw new Exception("新增数据集版本失败");
            }
        }
        return "新增数据集成功";
    }


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


    public void checkDeclaredName(Dataset insert) throws Exception {
        Dataset existingDataset = datasetDao.findByName(insert.getName());
        if (existingDataset != null) {
            if (insert.getId() != null && insert.getId().equals(existingDataset.getId())) {
                // 相同数据集，无需判断
                return;
            }

            Field[] fields = Dataset.class.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                if ("name".equals(field.getName()) && field.isAnnotationPresent(CheckDuplicate.class)) {
                    CheckDuplicate annotation = field.getAnnotation(CheckDuplicate.class);
                    throw new Exception("重复的数据集名称: " + insert.getName() + ". " + annotation.message());
                }
            }
        }
    }

    @Override
    public ResponseEntity<InputStreamResource>  downloadAllDatasetFiles(Integer datasetId, String version) throws Exception {
        // 根据数据集id查数据名
        Dataset dataset = this.datasetDao.queryById(datasetId);
        String datasetName = dataset.getName();
        // 查询特定模型和版本对应的所有文件
        List<DatasetVersion> datasetVersionList = this.datasetVersionDao.queryAllByDatasetVersion(datasetId, version);
        if (datasetVersionList == null || datasetVersionList.isEmpty()) {
            throw new Exception("数据集文件记录不存在！");
        }

        // 创建ZIP文件，准备打包下载
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(baos)) {
            //遍历每一个version数据项
            for (DatasetVersion datasetVersion : datasetVersionList) {
                String objectName = datasetVersion.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=\"" + datasetName + "_"  + version + ".zip\"")
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
    }

    @Override
    public List<Map<String, String>> exportDataset(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 = "datasets/" + 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;
    }

}
