package com.bonus.file.service.impl;

import com.alibaba.nacos.common.utils.UuidUtils;
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.ObsUtils;
import com.bonus.system.api.domain.SysFile;
import com.obs.services.model.ObsObject;
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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Minio 文件存储
 *
 * @author bonus
 */
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "obs")
public class ObsServiceImpl implements ISysFileService {
    @Resource
    private ObsUtils obsUtils;

    /**
     * 文件上传
     *
     * @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);
            SysFile sysFile = obsUtils.uploadFile(objectKey, Objects.requireNonNull(FileUtils.multipartFileToFile(file)));
            sysFile.setName(originalFilename);
            return sysFile;
        } catch (Exception e) {
           throw new Exception(e);
        }
    }

    @Override
    public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception {
        try {
            List<SysFile> sysFiles = new ArrayList<>();
            for (MultipartFile file : files) {
                SysFile sysFile = uploadFile(file);
                sysFiles.add(sysFile);
            }
            return sysFiles;
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

    @Override
    public void downloadFile(HttpServletResponse response, String urlStr) throws Exception {
        R<ObsObject> obsObjectR = obsUtils.downloadFile(urlStr);
        if (R.isError(obsObjectR)) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            throw new Exception("文件不存在");
        }
        if (obsObjectR.getData() == null) {
            response.setStatus(HttpStatus.NOT_FOUND.value());
            throw new Exception("文件不存在");
        }

        InputStream inputStream = obsObjectR.getData().getObjectContent();
        com.bonus.file.utils.FileUtils.setResponseHeaderByUrl(response, urlStr);
        try (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 (IOException e) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            throw new Exception("下载失败");
        }
    }

    /**
     * 从给定的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<ObsObject> obsObjectR = obsUtils.downloadFile(objectName);
                if (R.isError(obsObjectR)) {
                    response.setStatus(HttpStatus.BAD_REQUEST.value());
                    throw new Exception("文件不存在");
                }
                if (obsObjectR.getData() == null) {
                    response.setStatus(HttpStatus.NOT_FOUND.value());
                    throw new Exception("文件不存在");
                }
                try (InputStream inputStream = obsObjectR.getData().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 {
         obsUtils.deleteFile(urlStr);
    }

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

    }

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

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

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