package com.bonus.file.service.impl;

import com.alibaba.nacos.common.utils.UuidUtils;
import com.aliyun.oss.model.OSSObject;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.common.core.utils.file.MimeTypeUtils;
import com.bonus.file.entity.FileDetails;
import com.bonus.file.service.ISysFileService;
import com.bonus.file.utils.FileUploadUtils;
import com.bonus.file.utils.OssUtils;
import com.bonus.system.api.domain.SysFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author jiang
 */
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "oss")
public class OssServiceImpl implements ISysFileService {

    private static final Logger log = LoggerFactory.getLogger(OssServiceImpl.class);

    @Resource
    private OssUtils ossUtils;

    /**
     * 文件上传
     *
     * @param file 文件流
     * @return 文件信息
     */
    @Override
    public SysFile uploadFile(MultipartFile file)  throws Exception {
        try {
            //验证文件扩展名和大小
            FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
            String originalFilename = Objects.requireNonNull(file.getOriginalFilename(), "文件名不能为空");
            originalFilename = originalFilename.replace(" ", "");
            String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
            String objectKey = UuidUtils.generateUuid() + extension;
            objectKey = FileUtils.generateObjectName(objectKey);
            return ossUtils.upload(objectKey, FileUtils.multipartFileToFile(file));
        } catch (Exception e) {
            throw new Exception("上传文件异常:"+ e.getMessage());
        }
    }

    @Override
    public List<SysFile> uploadFiles(MultipartFile[] files) throws InterruptedException, ExecutionException {
            // 将每个文件的上传操作作为一个 CompletableFuture 任务
            List<CompletableFuture<SysFile>> futures = new ArrayList<>();

            for (MultipartFile file : files) {
                CompletableFuture<SysFile> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        return uploadFile(file);
                    } catch (Exception e) {
                        throw new RuntimeException("文件上传失败: " + file.getOriginalFilename(), e);
                    }
                });
                futures.add(future);
            }

            // 使用 allOf 等待所有任务完成，并收集结果
            CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
            // 在所有上传任务完成后，获取每个文件的上传结果
            return allOf.thenApply(v -> futures.stream()
                    .map(CompletableFuture::join)  // 获取每个 CompletableFuture 的结果
                    .collect(Collectors.toList())
            ).get();  // .get() 会阻塞直到所有任务完成
    }


    @Override
    public void downloadFile(HttpServletResponse response, String urlStr) throws Exception {
        R<OSSObject> ossObjectR = ossUtils.download(urlStr);
        if (ossObjectR.getData() == null) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            throw new Exception("未发现文件");
        }

        OSSObject ossObject = ossObjectR.getData();
        com.bonus.file.utils.FileUtils.setResponseHeaderByUrl(response, urlStr);
        try (InputStream inputStream = ossObject.getObjectContent();
             OutputStream outputStream = response.getOutputStream()) {

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush();
        } catch (Exception e) {
            log.error("文件下载过程中出现未知异常，原因是：", e);
            throw new Exception("文件下载过程中出现未知异常" );
        }
    }

    @Override
    public String getFullFileUrl(String urlStr) throws Exception {
        return urlStr;
    }

    /**
     * 从给定的URL下载文件并将其保存到指定的目标位置。
     *
     * @param response 请求响应。
     * @param urlStr   要下载文件的URL地址。
     * @throws Exception 如果在下载过程中遇到任何异常，例如网络问题、文件写入问题等。
     */
    @Override
    public void downloadFilesAsZip(HttpServletResponse response, List<String> urlStr) throws Exception {
        String zipFileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".zip";
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);
        try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            byte[] buffer = new byte[8192];
            for (String objectName : urlStr) {
                R<OSSObject> ossObjectR = ossUtils.download(objectName);
                if (ossObjectR.getData() == null) {
                    response.setStatus(HttpStatus.NOT_FOUND.value());
                    throw new Exception("未发现文件");
                }
                OSSObject ossObject = ossObjectR.getData();
                try (InputStream inputStream = ossObject.getObjectContent()) {
                    zos.putNextEntry(new ZipEntry(objectName));
                    int len;
                    while ((len = inputStream.read(buffer)) > 0) {
                        zos.write(buffer, 0, len);
                    }
                    zos.closeEntry();
                } catch (Exception e) {
                    System.err.println("无法压缩文件: " + objectName + " 错误: " + e.getMessage());
                }
            }
            zos.finish();
        }
    }

    @Override
    public void deleteFile(String urlStr) throws Exception {
        ossUtils.delete(urlStr);
    }

    /**
     * 删除指定URL所指向的文件。
     *
     * @param urlStr 要删除文件的URL地址。
     * @return 布尔值，指示文件删除是否成功。如果文件被成功删除，则返回true；否则返回false。
     * @throws Exception 如果在删除文件的过程中遇到错误，例如网络问题、文件不存在或没有删除权限等。
     */
    @Override
    public void deleteFiles(List<String> urlStr) throws Exception {
        for (String objectName : urlStr) {
            try {
                deleteFile(objectName);
            } catch (Exception e) {
                log.error("文件删除失败: {} 错误: {}", objectName, e.getMessage());
            }
        }
    }

    /**
     * 获取指定文件夹下的所有文件夹和文件。
     *
     * @param folderPath 要获取文件夹和文件的父文件夹路径（例如 "folder1/"）
     * @return 返回一个包含所有文件夹和文件路径的集合
     */
    @Override
    public Set<FileDetails> getFilesAndSubfolders(String folderPath) {
        return ossUtils.getFilesAndFolders(folderPath);
    }

    /**
     * 创建文件夹（通过上传带有路径的文件）
     *
     * @param folderPath 文件夹路径（例如 "folder1/subfolder/"）
     */
    @Override
    public void createFolder(String folderPath) {
            ossUtils.createFolder(folderPath);
    }

    /**
     * 删除指定路径的所有对象，从而删除“文件夹”。
     *
     * @param folderPath 文件夹路径（例如 "folder1/subfolder/"）
     */
    @Override
    public void deleteFolder(String folderPath) {
            ossUtils.deleteFolder(folderPath);
    }

}
