package com.yfd.platform.modules.storage.service.impl;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.yfd.platform.constant.ZFileConstant;
import com.yfd.platform.exception.init.InitializeStorageSourceException;
import com.yfd.platform.modules.storage.model.enums.FileTypeEnum;
import com.yfd.platform.modules.storage.model.enums.StorageTypeEnum;
import com.yfd.platform.modules.storage.model.param.LocalParam;
import com.yfd.platform.modules.storage.model.result.FileItemResult;
import com.yfd.platform.modules.storage.service.base.AbstractProxyTransferService;
import com.yfd.platform.utils.CodeMsg;
import com.yfd.platform.utils.RequestHolder;
import com.yfd.platform.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author zhengsl
 */
@Service
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Slf4j
public class LocalServiceImpl extends AbstractProxyTransferService<LocalParam> {

    private static final String PREVIEW_PARAM_NAME = "preview";

    @Override
    public void init() {
        // 初始化存储源
        File file = new File(param.getFilePath());
        // 校验文件夹是否存在
        if (!file.exists()) {
            String errMsg = StrUtil.format("文件路径:「{}」不存在, 请检查是否填写正确.", file.getAbsolutePath());
            throw new InitializeStorageSourceException(CodeMsg.STORAGE_SOURCE_INIT_FAIL,
                    storageId, errMsg).setResponseExceptionMessage(true);
        }
    }


    @Override
    public List<FileItemResult> fileList(String folderPath) throws FileNotFoundException {
        checkPathSecurity(folderPath);

        List<FileItemResult> fileItemList = new ArrayList<>();

        String fullPath = StringUtils.concat(param.getFilePath() + folderPath);

        File file = new File(fullPath);

        if (!file.exists()) {
            throw new FileNotFoundException("文件不存在");
        }

        File[] files = file.listFiles();

        if (files == null) {
            return fileItemList;
        }
        for (File f : files) {
            fileItemList.add(fileToFileItem(f, folderPath));
        }

        return fileItemList;
    }


    @Override
    public FileItemResult getFileItem(String pathAndName) {
        checkPathSecurity(pathAndName);

        String fullPath = StringUtils.concat(param.getFilePath(), pathAndName);

        File file = new File(fullPath);

        if (!file.exists()) {
            return new FileItemResult();
            //throw ExceptionUtil.wrapRuntime(new FileNotFoundException("文件不存在"));
        }

        String folderPath = StringUtils.getParentPath(pathAndName);
        return fileToFileItem(file, folderPath);
    }


    @Override
    public boolean newFolder(String path, String name) {
        checkPathSecurity(path);
        checkNameSecurity(name);

        String fullPath = StringUtils.concat(param.getFilePath(), path, name);
        return FileUtil.mkdir(fullPath) != null;
    }

    /**
     * 判断给定路径的文件夹是否已经创建
     *
     * @param folderPath 文件夹路径（例如 "/folderName"）
     * @return true - 文件夹已存在；false - 文件夹不存在
     */
    public boolean isFolderCreated(String folderPath) {
        // 将路径转换为绝对路径（如果需要）
        Path path = Paths.get(folderPath).toAbsolutePath();

        // 检查路径是否存在且是一个目录
        return Files.exists(path) && Files.isDirectory(path);
    }


    @Override
    public boolean deleteFile(String path, String name) {
        checkPathSecurity(path);
        checkNameSecurity(name);

        String fullPath = StringUtils.concat(param.getFilePath(), path, name);
        return FileUtil.del(fullPath);
    }


    @Override
    public boolean deleteFolder(String path, String name) {
        checkPathSecurity(path);
        checkNameSecurity(name);

        return deleteFile(path, name);
    }


    @Override
    public boolean renameFile(String path, String name, String newName) {
        checkPathSecurity(path);
        checkNameSecurity(name, newName);

        // 如果文件名没变，不做任何操作.
        if (StrUtil.equals(name, newName)) {
            return true;
        }

        String srcPath = StringUtils.concat(param.getFilePath(), path, name);
        File file = new File(srcPath);

        boolean srcExists = file.exists();
        if (!srcExists) {
            throw ExceptionUtil.wrapRuntime(new FileNotFoundException("文件夹不存在."));
        }

        FileUtil.rename(file, newName, true);
        return true;
    }


    @Override
    public boolean renameFolder(String path, String name, String newName) {
        checkPathSecurity(path);
        checkNameSecurity(name, newName);

        return renameFile(path, name, newName);
    }


    @Override
    public StorageTypeEnum getStorageTypeEnum() {
        return StorageTypeEnum.LOCAL;
    }

    @Override
    public boolean UploadFiles(String name, String path, File zipFile) {
        return false;
    }

    @Override
    public ObjectMetadata getObjectMetadata(String name, String path) {
        return null;
    }

    @Override
    public S3Object getObject(String bucketName, String key) {
        return null;
    }


    @Override
    public List<FileItemResult> fileListData(String folderPath, String name) throws Exception {
        checkPathSecurity(folderPath);

        List<FileItemResult> resultList = new ArrayList<>();
        String basePath = param.getFilePath();

        // 1. 构建目标路径（动态判断是否为目录）
        String targetPath = formatCombinedPath(folderPath, name);
        String fullPath = Paths.get(basePath, targetPath).toString();

        File target = new File(fullPath);
        if (!target.exists()) {
            throw new FileNotFoundException("路径不存在: " + fullPath);
        }

        // 2. 直接处理文件或目录
        if (target.isDirectory()) {
            // 添加目标文件夹自身
            FileItemResult folderItem = convertToFileItem(target, folderPath);
            resultList.add(folderItem);

            // 递归列出目录内容
            listFilesInDirectory(target, targetPath, resultList);
        } else {
            // 如果是文件，直接添加到结果列表
            FileItemResult item = convertToFileItem(target, folderPath);
            resultList.add(item);
        }

        return resultList;
    }

    /**
     * 构建规范化路径（根路径为 /，其他路径为 /path/）
     */
    private String formatCombinedPath(String folderPath, String name) {
        // 确保 folderPath 以 "/" 开头，如果不是，则添加 "/"
        if (folderPath == null || folderPath.trim().isEmpty()) {
            folderPath = "/";
        } else {
            folderPath = folderPath.trim();
            if (!folderPath.startsWith("/")) {
                folderPath = "/" + folderPath;
            }
            // 去掉 folderPath 末尾的斜杠（除非是根路径）
            if (!folderPath.equals("/") && folderPath.endsWith("/")) {
                folderPath = folderPath.substring(0, folderPath.length() - 1);
            }
        }

        // 如果 name 非空，拼接到 folderPath 后
        if (name != null && !name.trim().isEmpty()) {
            folderPath += "/" + name;
        }

        return folderPath;

//        StringBuilder pathBuilder = new StringBuilder();
//
//        // 处理 folderPath
//        if (StringUtils.isBlank(folderPath)) {
//            pathBuilder.append("/");
//        } else {
//            // 确保 folderPath 以 / 开头且不以 / 结尾（除非是根路径）
//            String trimmedPath = folderPath.trim();
//            if (!trimmedPath.startsWith("/")) {
//                pathBuilder.append("/");
//            }
//            pathBuilder.append(trimmedPath.replaceAll("/+$", ""));
//            if (!trimmedPath.endsWith("/") && !trimmedPath.equals("/")) {
//                pathBuilder.append("/");
//            }
//        }
//
//        // 处理 name（如果是文件，不添加 /）
//        if (StringUtils.isNotBlank(name)) {
//            String sanitizedName = name.trim().replaceAll("/+", ""); // 防止路径注入
//            pathBuilder.append(sanitizedName);
//
//            // 如果是目录，添加 /
//            File tempFile = new File(Paths.get(param.getFilePath(), folderPath, sanitizedName).toString());
//            if (tempFile.isDirectory()) {
//                pathBuilder.append("/");
//            }
//        }
//
//        // 最终格式化（合并多斜杠）
//        return pathBuilder.toString().replaceAll("/+", "/");
    }

    /**
     * 递归列出目录内容
     */
    private void listFilesInDirectory(File directory, String parentPath, List<FileItemResult> resultList) {
        File[] files = directory.listFiles();
        if (files == null) return;

        for (File file : files) {
            // 创建文件项（确保路径格式）
            FileItemResult item = convertToFileItem(file, parentPath);
            resultList.add(item);

            // 递归处理子目录
            if (file.isDirectory()) {
                String newParentPath = parentPath + "/" + file.getName() + "/";
                listFilesInDirectory(file, newParentPath, resultList);
            }
        }
    }

    /**
     * 转换文件对象为结果对象（路径前后带 /）
     */
    private FileItemResult convertToFileItem(File file, String parentPath) {
        FileItemResult item = new FileItemResult();
        item.setName(file.getName());
        item.setPath(parentPath);
        item.setSize(file.isDirectory() ? 0 : file.length());
        item.setTime(new Date(file.lastModified()));
        item.setType(file.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);

        // 如果是文件，设置下载 URL
        if (file.isFile()) {
            item.setUrl(getDownloadUrl(parentPath + file.getName()));
        }

        return item;
    }

    /**
     * 单一路径格式化规则：
     * - 根路径返回 /
     * - 其他路径返回 /path/
     */
    private String formatSinglePath(String path) {
        if (StringUtils.isBlank(path)) return "/";

        // 替换多斜杠为单斜杠
        path = path.replaceAll("/+", "/");

        // 确保以 / 开头
        if (!path.startsWith("/")) {
            path = "/" + path;
        }

        // 根路径直接返回
        if (path.equals("/")) return path;

        // 确保以 / 结尾
        if (!path.endsWith("/")) {
            path += "/";
        }

        return path;
    }

    //以下是通过路径获取所有的文件以及文件夹
    @Override
    public List<FileItemResult> fileLists(String folderPath) throws Exception {
        checkPathSecurity(folderPath);

        List<FileItemResult> fileItemList = new ArrayList<>();

        String fullPath = StringUtils.concat(param.getFilePath() + folderPath);

        File file = new File(fullPath);

        if (!file.exists()) {
            throw new FileNotFoundException("文件不存在");
        }

        // 调用递归方法处理文件夹及其内容（跳过第一个文件夹）
        listFilesInDirectory(file, folderPath, fileItemList, true);

        return fileItemList;
    }

    private void listFilesInDirectory(File file, String folderPath, List<FileItemResult> fileItemList, boolean skipFirstFolder) throws IOException {
        // 跳过第一个文件夹（folderPath），从它下面的内容开始处理
        if (skipFirstFolder) {
            // 如果当前文件是 folderPath, 直接跳过，开始处理它的子文件夹
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                if (files != null) {
                    for (File f : files) {
                        // 递归进入下一级文件夹，路径应该是 folderPath + 当前文件夹名
                        listFilesInDirectory(f, folderPath, fileItemList, false);
                    }
                }
            }
        } else {
            // 处理当前的文件夹或文件
            if (file.isDirectory()) {
                // 对于文件夹，路径是 folderPath
                fileItemList.add(fileToFileItems(file, folderPath));

                // 递归处理当前文件夹内部的文件和文件夹
                File[] files = file.listFiles();
                if (files != null) {
                    for (File f : files) {
                        // 递归进入下一级文件夹，路径是 folderPath + 当前文件夹名
                        String newPath = folderPath  + file.getName();
                        listFilesInDirectory(f, newPath, fileItemList, false);
                    }
                }
            } else {
                // 对于文件，路径是 folderPath
                fileItemList.add(fileToFileItems(file, folderPath));
            }
        }
    }
    //lilin增加
    private FileItemResult fileToFileItems(File file, String folderPath) {
        FileItemResult result = new FileItemResult();
        result.setName(file.getName());
        result.setPath(folderPath); // 这里返回的是父目录路径，不包含文件名

        // 使用 FileTypeEnum 设置文件类型
        if (file.isDirectory()) {
            result.setType(FileTypeEnum.FOLDER);
        } else {
            result.setType(FileTypeEnum.FILE);
            if (file.isFile()) {
                result.setUrl(getDownloadUrl(folderPath + file.getName()));
            }
        }

        result.setSize(file.length());
        // 设置文件的修改时间为 Date 类型
        result.setTime(new Date(file.lastModified()));

        return result;
    }


    @Override
    public void uploadFile(String pathAndName, InputStream inputStream) {
        checkPathSecurity(pathAndName);

        String baseFilePath = param.getFilePath();
        String uploadPath = StringUtils.removeDuplicateSlashes(baseFilePath + ZFileConstant.PATH_SEPARATOR + pathAndName);
        // 如果目录不存在则创建
        String parentPath = FileUtil.getParent(uploadPath, 1);
        if (!FileUtil.exist(parentPath)) {
            FileUtil.mkdir(parentPath);
        }

//        File uploadToFileObj = new File(uploadPath);
//        BufferedOutputStream outputStream = FileUtil.getOutputStream(uploadToFileObj);
//        IoUtil.copy(inputStream, outputStream);
//        IoUtil.close(outputStream);
//        IoUtil.close(inputStream);
//        try (BufferedOutputStream outputStream = FileUtil.getOutputStream(new File(uploadPath))) {
//            IoUtil.copy(inputStream, outputStream);
//        } catch (IOException e) {
//            throw new RuntimeException("文件保存失败", e);
//        } finally {
//            IoUtil.close(inputStream);
//        }

        try (BufferedOutputStream outputStream = FileUtil.getOutputStream(new File(uploadPath))) {
            byte[] buffer = new byte[8192];  // 8KB 缓冲区
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new RuntimeException("文件保存失败", e);
        } finally {
            IoUtil.close(inputStream);
        }
    }


    @Override
    public ResponseEntity<Resource> downloadToStream(String pathAndName) {
        checkPathSecurity(pathAndName);

        File file = new File(StringUtils.removeDuplicateSlashes(param.getFilePath() + ZFileConstant.PATH_SEPARATOR + pathAndName));
        if (!file.exists()) {
            ByteArrayResource byteArrayResource = new ByteArrayResource("文件不存在或异常，请联系管理员.".getBytes(StandardCharsets.UTF_8));
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .body(byteArrayResource);
        }

        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        String type = request.getParameter("type");

        MediaType mimeType = MediaType.APPLICATION_OCTET_STREAM;
        if (StrUtil.equals(type, PREVIEW_PARAM_NAME)) {
            mimeType = MediaTypeFactory.getMediaType(file.getName()).orElse(MediaType.APPLICATION_OCTET_STREAM);
        }

        HttpHeaders headers = new HttpHeaders();

        if (ObjectUtil.equals(mimeType, MediaType.APPLICATION_OCTET_STREAM)) {
            String fileName = file.getName();
            headers.setContentDispositionFormData("attachment", StringUtils.encodeAllIgnoreSlashes(fileName));
        }

        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.length())
                .contentType(mimeType)
                .body(new FileSystemResource(file));
    }


    private FileItemResult fileToFileItem(File file, String folderPath) {
        FileItemResult fileItemResult = new FileItemResult();
        fileItemResult.setType(file.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
        fileItemResult.setTime(new Date(file.lastModified()));
        fileItemResult.setSize(file.length());
        fileItemResult.setName(file.getName());
        fileItemResult.setPath(folderPath);

        if (fileItemResult.getType() == FileTypeEnum.FILE) {
            fileItemResult.setUrl(getDownloadUrl(StringUtils.concat(folderPath, file.getName())));
        } else {
            fileItemResult.setSize(null);
        }
        return fileItemResult;
    }


    /**
     * 检查路径合法性：
     * - 只有以 . 开头的允许通过，其他的如 ./ ../ 的都是非法获取上层文件夹内容的路径.
     *
     * @param paths 文件路径
     * @throws IllegalArgumentException 文件路径包含非法字符时会抛出此异常
     */
    private static void checkPathSecurity(String... paths) {
        for (String path : paths) {
            // 路径中不能包含 .. 不然可能会获取到上层文件夹的内容
            if (StrUtil.startWith(path, "/..") || StrUtil.containsAny(path, "../", "..\\")) {
                throw new IllegalArgumentException("文件路径存在安全隐患: " + path);
            }
        }
    }


    /**
     * 检查路径合法性：
     * - 不为空，且不包含 \ / 字符
     *
     * @param names 文件路径
     * @throws IllegalArgumentException 文件名包含非法字符时会抛出此异常
     */
    private static void checkNameSecurity(String... names) {
        for (String name : names) {
            // 路径中不能包含 .. 不然可能会获取到上层文件夹的内容
            if (StrUtil.containsAny(name, "\\", "/")) {
                throw new IllegalArgumentException("文件名存在安全隐患: " + name);
            }
        }
    }

}
