package org.example.Utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.Part;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

//附件工具类,(转换C#反编译方法)
public class RequestUtil {
    private static final Logger log = LoggerFactory.getLogger(RequestUtil.class);


    //    文件上传,获取文件名等工具
// 请求级别的流缓存（ThreadLocal避免多请求串流）
    private static final ThreadLocal<byte[]> rawContentCache = new ThreadLocal<>();
    private static final ThreadLocal<ReadEntityBodyMode> readEntityBodyMode = new ThreadLocal<>();

    /**
     * （类似C#的Request.Files.Count）
     * 获取文件数量
     *
     * @param request 请求对象
     */
    public static boolean hasUploadedFiles(HttpServletRequest request) {
        String contentType = request.getContentType();

        // 处理multipart/form-data类型
        if (contentType != null && contentType.startsWith("multipart/form-data")) {
            return hasMultipartFiles(request);
        }
        // 处理application/octet-stream类型
        else if (contentType != null && contentType.equals("application/octet-stream")) {
//            C#对应Request.Files.Count 始终为 0,纯二进制流
//            return request.getContentLength() > 0;
            return false;
        }

        return false;
    }

    /**
     * 获取所有有效上传文件的文件名
     */
    public static List<String> getUploadedFileNames(HttpServletRequest request) {
        List<String> fileNames = new ArrayList<>();
        String contentType = request.getContentType();

        // 处理multipart/form-data类型
        if (contentType != null && contentType.startsWith("multipart/form-data")) {
            try {
                for (Part part : request.getParts()) {
                    String fileName = getSubmittedFileName(part);
                    if (fileName != null && !fileName.isEmpty() && part.getSize() > 0) {
                        fileNames.add(fileName);
                    }
                }
            } catch (Exception e) {
                log.error("Exception caught", e);
            }
        }
        // 处理application/octet-stream类型（生成默认文件名）
        else if (contentType != null && contentType.equals("application/octet-stream") && request.getContentLength() > 0) {
            // 二进制流没有文件名，生成一个UUID作为文件名
            fileNames.add(generateDefaultFileName());
        }

        return fileNames;
    }

    /**
     * 保存上传的文件到指定目录
     *
     * @param request 请求对象
     * @param saveDir 保存目录路径
     * @return 成功保存的文件名列表
     */
    public static List<String> saveUploadedFiles(HttpServletRequest request, String saveDir) {
        List<String> savedFiles = new ArrayList<>();
        String contentType = request.getContentType();

        // 确保保存目录存在
        try {
            Files.createDirectories(Paths.get(saveDir));
        } catch (IOException e) {
            log.error("Exception caught", e);
            return savedFiles;
        }

        // 处理multipart/form-data类型
        if (contentType != null && contentType.startsWith("multipart/form-data")) {
            try {
                for (Part part : request.getParts()) {
                    String fileName = getSubmittedFileName(part);
                    if (fileName != null && !fileName.isEmpty() && part.getSize() > 0) {
                        // 构建完整保存路径
                        String savePath = saveDir.endsWith("/") ? saveDir + fileName : saveDir + "/" + fileName;
                        part.write(savePath);
                        savedFiles.add(fileName);
                    }
                }
            } catch (Exception e) {
                log.error("Exception caught", e);
            }
        }
        // 处理application/octet-stream类型
        else if (contentType != null && contentType.equals("application/octet-stream") && request.getContentLength() > 0) {
            try {
                String fileName = generateDefaultFileName();
                String savePath = saveDir.endsWith("/") ? saveDir + fileName : saveDir + "/" + fileName;
                // 直接从输入流读取并保存
                try (InputStream in = request.getInputStream()) {
                    Files.copy(in, Paths.get(savePath));
                    savedFiles.add(fileName);
                }
            } catch (Exception e) {
                log.error("Exception caught", e);
            }
        }

        return savedFiles;
    }

    /**
     * 获取上传文件的原始文件名（兼容不同 Servlet 版本）
     */
    public static String getSubmittedFileName(Part part) {
        // Jakarta Servlet 3.1+ 原生方法
        String fileName = part.getSubmittedFileName();
        if (fileName == null) {
            // 从请求头解析文件名（兼容旧版本）
            String contentDisposition = part.getHeader("Content-Disposition");
            if (contentDisposition != null) {
                for (String token : contentDisposition.split(";")) {
                    token = token.trim();
                    if (token.startsWith("filename=")) {
                        return token.substring("filename=".length())
                                .replace("\"", "")
                                .trim();
                    }
                }
            }
            return "";
        }
        return fileName;
    }

    /**
     * （类似C#的Request.Count）
     * 获取文件数量
     *
     * @param request 请求对象
     */
    public static int getUploadedFileCount(HttpServletRequest request) {
        String contentType = request.getContentType();

        // 处理multipart/form-data类型
        if (contentType != null && contentType.startsWith("multipart/form-data")) {
            int count = 0;
            try {
                for (Part part : request.getParts()) {
                    // 检查是否为有效文件（有文件名且大小 > 0）
                    String fileName = getSubmittedFileName(part);
                    if (fileName != null && !fileName.isEmpty() && part.getSize() > 0) {
                        count++; // 符合条件则计数+1
                    }
                }
            } catch (Exception e) {
                log.error("Exception caught", e);
            }
            return count;
        }
        // 处理application/octet-stream类型（要么1个要么0个）
        else if (contentType != null && contentType.equals("application/octet-stream")) {
            return request.getContentLength() > 0 ? 1 : 0;
        }

        return 0;
    }

    /**
     * 按索引获取上传的文件信息综合体（同时支持multipart和octet-stream）
     * 功能与C#的Request.Files[i]完全一致
     *
     * @param request 请求对象
     * @param index   索引（从0开始）
     * @return 文件信息综合体（multipart返回Part，octet-stream返回OctetStreamFile）
     */
    public static Object getUploadedFileByIndex(HttpServletRequest request, int index) {
        String contentType = request.getContentType();
        if (contentType == null) {
            return null;
        }

        // 处理multipart/form-data类型（通过Part返回）
        if (contentType.startsWith("multipart/form-data")) {
            try {
                List<Part> validFiles = new ArrayList<>();
                for (Part part : request.getParts()) {
                    String fileName = getSubmittedFileName(part);
                    if (fileName != null) { // 筛选文件类型Part
                        validFiles.add(part);
                    }
                }
                if (index >= 0 && index < validFiles.size()) {
                    return validFiles.get(index); // Part是multipart格式的文件信息综合体
                }
            } catch (Exception e) {
                log.error("Exception caught", e);
            }
            return null;
        }

        // 处理application/octet-stream类型（通过自定义类返回）
        if (contentType.equals("application/octet-stream")) {
            // octet-stream通常一次请求只传一个文件，索引只能为0
            if (index != 0) {
                return null;
            }

            try {
                // 从请求头获取文件名（前端需通过X-FileName传递，如无则用默认名）
                String fileName = request.getHeader("X-FileName");
                log.debug(String.valueOf("fileName : " + fileName));
                if (fileName == null || fileName.trim().isEmpty()) {
                    fileName = "unknown_octet_stream.bin";
                }

                // 构建文件信息综合体（包含元信息和二进制流）
                return new OctetStreamFile(
                        fileName,
                        contentType,
                        request.getContentLengthLong(),
                        request.getInputStream()
                );
            } catch (IOException e) {
                log.error("Exception caught", e);
                return null;
            }
        }

        // 不支持的Content-Type
        return null;
    }

    /**
     * 自定义类：封装application/octet-stream类型的文件信息综合体
     * 功能与Part和C#的HttpPostedFile对齐
     */
    public static class OctetStreamFile {
        private final String fileName;
        private final String contentType;
        private final long contentLength;
        private final java.io.InputStream inputStream;

        public OctetStreamFile(String fileName, String contentType, long contentLength, java.io.InputStream inputStream) {
            this.fileName = fileName;
            this.contentType = contentType;
            this.contentLength = contentLength;
            this.inputStream = inputStream;
        }

        // 元信息获取方法（对应C#的HttpPostedFile属性）
        public String getFileName() {
            return fileName;
        }

        public String getContentType() {
            return contentType;
        }

        public long getContentLength() {
            return contentLength;
        }

        // 核心：获取文件二进制流（对应C#的InputStream）
        public java.io.InputStream getInputStream() {
            return inputStream;
        }

        // 保存文件到磁盘（对应C#的SaveAs方法）
        public void saveAs(String filePath) throws IOException {
            try (java.io.InputStream is = inputStream;
                 java.io.FileOutputStream fos = new java.io.FileOutputStream(filePath)) {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, bytesRead);
                }
            }
        }
    }

    // 线程局部变量存储已读取的实体流（对应C#的_readEntityBodyStream）
    private static final ThreadLocal<ByteArrayInputStream> cachedEntityStream = new ThreadLocal<>();

    /**
     * 获取请求总字节数（类似C# TotalBytes逻辑）
     * 优先级：已缓存的实体流 > 原始输入流，流为null时返回0
     *
     * @param request HTTP请求对象
     * @return 请求总字节数
     * @throws IOException 流操作异常
     */
    public static int getTotalBytes(HttpServletRequest request) throws IOException {
        // 1. 优先获取已缓存的实体流（对应C#的_readEntityBodyStream）
        InputStream stream = cachedEntityStream.get();

        // 2. 若缓存流不存在，使用原始输入流并缓存
        if (stream == null) {
            // 读取原始输入流内容到字节数组（因Servlet流只能读一次，需缓存）
            try (InputStream originalStream = request.getInputStream();
                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = originalStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }

                // 将字节数组转为输入流并缓存
                byte[] contentBytes = outputStream.toByteArray();
                stream = new ByteArrayInputStream(contentBytes);
                cachedEntityStream.set((ByteArrayInputStream) stream);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        // 3. 若流仍为null，返回0；否则返回流长度（对应C#的stream.Length）
        if (stream == null) {
            return 0;
        }

        // ByteArrayInputStream的available()返回总字节数，等价于C#的stream.Length
        return stream instanceof ByteArrayInputStream ? ((ByteArrayInputStream) stream).available() : 0;
    }

    /**
     * 获取当前使用的流（供后续读取内容使用，避免重复读取原始流）
     * 对应C#中使用TotalBytes后仍可读取流内容的场景
     *
     * @return 已缓存的流或原始流
     */
    public static InputStream getCurrentStream() {
        return cachedEntityStream.get();
    }

    /**
     * 清理线程缓存的流（必须在请求处理结束后调用，防止内存泄漏）
     */
    public static void clearCachedStream() {
        cachedEntityStream.remove();
    }


    // 模拟C#的_readEntityBodyMode枚举（读取实体的模式）
    public enum ReadEntityBodyMode {
        Bufferless,  // 无缓冲模式（不允许获取缓冲流）
        Buffered     // 缓冲模式（允许获取缓冲流）
    }

    // 线程局部变量存储缓存的输入流（对应C#的_inputStream）
    private static final ThreadLocal<InputStream> cachedInputStream = new ThreadLocal<>();
    // 线程局部变量存储读取模式（对应C#的_readEntityBodyMode）
    //private static final ThreadLocal<ReadEntityBodyMode> readEntityBodyMode = ThreadLocal.withInitial(() -> ReadEntityBodyMode.Buffered);
    // 线程局部变量标记是否有有效的工作器请求（对应C#的_wr）
    private static final ThreadLocal<Boolean> hasWorkerRequest = new ThreadLocal<>();


    /**
     * 获取请求输入流（类似C#的InputStream属性）
     *
     * @param request HTTP请求对象
     * @return 缓存的或新创建的输入流
     * @throws ServletException 当模式为Bufferless时抛出（对应C#的HttpException）
     * @throws IOException      流操作异常
     */
    public static InputStream getInputStream1(HttpServletRequest request) throws ServletException, IOException {
        // 1. 检查缓存的输入流是否已存在，存在则直接返回（对应C#的if (_inputStream != null) return _inputStream）
        InputStream inputStream = cachedInputStream.get();
        if (inputStream != null) {
            return inputStream;
        }

        // 2. 检查读取模式，若为Bufferless则抛出异常（对应C#的_readEntityBodyMode == Bufferless判断）
        if (readEntityBodyMode.get() == ReadEntityBodyMode.Bufferless) {
            throw new ServletException("与无缓冲输入流不兼容（Incompatible with get_bufferless_input_stream）");
        }

        // 3. 获取完整的原始内容（对应C#的GetEntireRawContent()）
        byte[] rawContent = getEntireRawContent(request);

        // 4. 根据原始内容创建输入流并缓存（对应C#的创建HttpInputStream逻辑）
        if (rawContent != null) {
            inputStream = new ByteArrayInputStream(rawContent);
        } else {
            inputStream = new ByteArrayInputStream(new byte[0]); // 空流
        }
        cachedInputStream.set(inputStream);

        return inputStream;
    }

    /**
     * 安全获取请求输入流（解决缓存复用、空流、重置问题）
     *
     * @param request HttpServletRequest
     * @return 可重置的输入流
     * @throws ServletException 无缓冲模式异常
     * @throws IOException      流读取异常
     */
    public static InputStream getInputStream(HttpServletRequest request) throws ServletException, IOException {
        // 1. 检查无缓冲模式（直接抛异常）
        if (readEntityBodyMode.get() == ReadEntityBodyMode.Bufferless) {
            throw new ServletException("与无缓冲输入流不兼容（Incompatible with get_bufferless_input_stream）");
        }

        // 2. 优先从缓存获取原始字节数组（避免重复读取请求体）
        byte[] rawContent = rawContentCache.get();
        if (rawContent == null) {
            // 3. 读取完整请求体并缓存（仅读取一次）
            rawContent = getEntireRawContent(request);
            // 空值校验：避免返回null，用空字节数组并记录日志
            if (rawContent == null) {
                rawContent = new byte[0];
                log.warn(String.valueOf("警告：getEntireRawContent 返回null，已替换为空字节数组（请求URI：" + request.getRequestURI() + "）"));
            }
            rawContentCache.set(rawContent);
        }

        // 4. 创建可重置的ByteArrayInputStream（每次返回新流）
        ByteArrayInputStream inputStream = new ByteArrayInputStream(rawContent);

        // 5. 空流日志提示（便于排查上传空文件问题）
        if (rawContent.length == 0) {
            String encode = request.getParameter("encode");
            String filename = request.getParameter("filename");
            log.warn(String.valueOf(String.format("警告：请求体为空（encode=%s，filename=%s）",
                    encode == null ? "未知" : encode,
                    filename == null ? "未知" : filename)));
        }

        return inputStream;
    }

    /**
     * 获取完整的原始请求内容（模拟C#的GetEntireRawContent()）
     *
     * @param request HTTP请求对象
     * @return 原始内容的字节数组（null表示无内容）
     * @throws IOException 流操作异常
     */
    private static byte[] getEntireRawContent1(HttpServletRequest request) throws IOException {
        // 若没有有效的工作器请求（_wr == null），返回null（对应C#逻辑）
        if (Boolean.FALSE.equals(hasWorkerRequest.get())) {
            return null;
        }

        // 读取原始输入流的全部内容（模拟获取完整原始内容）
        try (InputStream originalStream = request.getInputStream();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = originalStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            return outputStream.toByteArray();
        }
    }

    /**
     * 读取完整请求体（兼容所有JDK版本，避免readAllBytes()兼容问题）
     *
     * @param request HttpServletRequest
     * @return 完整请求体字节数组
     * @throws IOException 流读取异常
     */
    private static byte[] getEntireRawContent(HttpServletRequest request) throws IOException {
        try (InputStream inputStream = request.getInputStream();
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            log.warn(String.valueOf("读取请求体失败：" + e.getMessage()));
            throw e;
        }
    }

    /**
     * 清理流缓存（上传完成后调用，避免内存泄漏）
     */
    public static void clearInputStreamCache() {
        rawContentCache.remove();
        readEntityBodyMode.remove();
    }
    // ------------------- 辅助方法（用于设置状态） -------------------

    /**
     * 设置读取实体的模式（对应C#的_readEntityBodyMode）
     *
     * @param mode 读取模式
     */
    public static void setReadEntityBodyMode(ReadEntityBodyMode mode) {
        readEntityBodyMode.set(mode);
    }

    /**
     * 设置是否有有效的工作器请求（对应C#的_wr）
     *
     * @param has 布尔值
     */
    public static void setHasWorkerRequest(boolean has) {
        hasWorkerRequest.set(has);
    }

    /**
     * 清理线程缓存的资源（必须在请求处理结束后调用）
     */
    public static void clear() {
        rawContentCache.remove();
        cachedInputStream.remove();
        readEntityBodyMode.remove();
        hasWorkerRequest.remove();
        clearCachedStream(); // 同时清理实体流缓存
    }

    /**
     * 生成默认文件名（用于application/octet-stream类型）
     */
    private static String generateDefaultFileName() {
        // 使用UUID生成唯一文件名，默认后缀为bin
        return UUID.randomUUID().toString() + ".bin";
    }

    /**
     * 检查请求是否为multipart/form-data类型
     */
    private static boolean hasMultipartFiles(HttpServletRequest request) {
        try {
            for (Part part : request.getParts()) {
                String fileName = getSubmittedFileName(part);
                if (fileName != null && !fileName.isEmpty() && part.getSize() > 0) {
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("Exception caught", e);
        }
        return false;
    }
}
