package com.njcn.cssystem.utils;

import com.njcn.common.pojo.exception.BusinessException;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * SVG压缩包处理器
 * 专门处理ZIP压缩包中的SVG文件
 */
@Component
public class SvgZipProcessor {

    // SVG文件头检查
    private static final String SVG_HEADER = "<?xml";
    private static final String SVG_START_TAG = "<svg";
    private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(
            Arrays.asList(".svg", ".svgz")
    );

    /**
     * 方法1：验证并提取所有SVG文件
     * 返回格式：Map<文件名, InputStream>
     */
    public static Map<String, InputStream> extractValidSvgFiles(MultipartFile zipFile) throws IOException, InvalidSvgException {
        Map<String, InputStream> svgFiles = new LinkedHashMap<>();
        ZipArchiveInputStream zis = null;
        ZipArchiveEntry entry = null;
        try {
            // 使用支持编码检测的ZipArchiveInputStream
            zis = new ZipArchiveInputStream(zipFile.getInputStream(), "UTF-8", true);
            byte[] buffer = new byte[8192];
            while ((entry = zis.getNextZipEntry()) != null) {
                if (!entry.isDirectory() && !isSvgFile(entry.getName())) {
                    throw new BusinessException("文件格式错误: " + entry.getName());
                }
                if (!entry.isDirectory() && isSvgFile(entry.getName())) {
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        baos.write(buffer, 0, len);
                    }
                    byte[] fileData = baos.toByteArray();
                    String fileName = entry.getName();
                    // 验证SVG文件
                    if (validateSvgFile(fileName, fileData)) {
                        ByteArrayInputStream svgStream = new ByteArrayInputStream(fileData);
                        svgFiles.put(fileName, svgStream);
                    } else {
                        throw new InvalidSvgException("无效的SVG文件: " + fileName);
                    }
                    baos.close();
                }
            }
        } catch (BusinessException e) {
            throw new BusinessException("文件格式错误: " + entry.getName());
        } catch (Exception e) {
            throw new IOException("处理ZIP文件时出错", e);
        } finally {
            // 确保流被关闭
            if (zis != null) {
                try {
                    zis.close();
                } catch (IOException e) {
                    // 记录日志但不影响主要逻辑
                    System.err.println("关闭ZipArchiveInputStream时出错: " + e.getMessage());
                }
            }
        }
        if (svgFiles.isEmpty()) {
            throw new InvalidSvgException("压缩包中没有找到有效的SVG文件");
        }
        return svgFiles;
    }

    /**
     * 方法2：流式处理SVG文件
     */
    public void processSvgFilesStreaming(MultipartFile zipFile,
                                                BiConsumer<String, InputStream> processor)
            throws IOException, InvalidSvgException {

        List<String> invalidFiles = new ArrayList<>();

        try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) {
            ZipEntry entry;
            byte[] buffer = new byte[8192];

            while ((entry = zis.getNextEntry()) != null) {
                if (!entry.isDirectory() && isSvgFile(entry.getName())) {

                    // 读取并验证
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        baos.write(buffer, 0, len);
                    }

                    byte[] fileData = baos.toByteArray();

                    if (validateSvgFile(entry.getName(), fileData)) {
                        ByteArrayInputStream svgStream = new ByteArrayInputStream(fileData);
                        processor.accept(entry.getName(), svgStream);
                        svgStream.close();
                    } else {
                        invalidFiles.add(entry.getName());
                    }

                    baos.close();
                }
                zis.closeEntry();
            }
        }

        // 如果有无效文件，抛出异常
        if (!invalidFiles.isEmpty()) {
            throw new InvalidSvgException("发现无效的SVG文件: " + invalidFiles);
        }
    }

    /**
     * 方法3：严格的SVG验证和处理
     */
    public List<SvgFileInfo> processAndValidateSvgFiles(MultipartFile zipFile,
                                                               ValidationOptions options)
            throws IOException, InvalidSvgException {

        List<SvgFileInfo> svgFiles = new ArrayList<>();
        List<String> validationErrors = new ArrayList<>();

        try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) {
            ZipEntry entry;
            byte[] buffer = new byte[8192];

            while ((entry = zis.getNextEntry()) != null) {
                String fileName = entry.getName();

                // 跳过目录和非SVG文件
                if (entry.isDirectory() || !isSvgFile(fileName)) {
                    if (!entry.isDirectory() && options.isStrictMode()) {
                        validationErrors.add("非SVG文件: " + fileName);
                    }
                    continue;
                }

                // 读取文件内容
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int len;
                while ((len = zis.read(buffer)) > 0) {
                    baos.write(buffer, 0, len);
                }

                byte[] fileData = baos.toByteArray();

                // 执行验证
                ValidationResult result = validateSvgWithDetails(fileName, fileData, options);

                if (result.isValid()) {
                    SvgFileInfo svgInfo = new SvgFileInfo(
                            fileName,
                            new ByteArrayInputStream(fileData),
                            fileData.length,
                            entry.getTime(),
                            result.getSvgAttributes()
                    );
                    svgFiles.add(svgInfo);
                } else {
                    validationErrors.add(fileName + ": " + result.getErrorMessage());
                }

                baos.close();
                zis.closeEntry();
            }
        }

        // 检查验证结果
        if (!validationErrors.isEmpty()) {
            throw new InvalidSvgException("SVG验证失败:\n" +
                    String.join("\n", validationErrors));
        }

        if (svgFiles.isEmpty()) {
            throw new InvalidSvgException("压缩包中没有有效的SVG文件");
        }

        return svgFiles;
    }

    /**
     * 方法4：仅获取SVG文件名
     */
    public List<String> getSvgFileNames(MultipartFile zipFile) throws IOException {
        List<String> svgNames = new ArrayList<>();

        try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) {
            ZipEntry entry;

            while ((entry = zis.getNextEntry()) != null) {
                if (!entry.isDirectory() && isSvgFile(entry.getName())) {
                    svgNames.add(entry.getName());
                }
                zis.closeEntry();
            }
        }

        return svgNames;
    }

    /**
     * 验证是否为SVG文件（基于扩展名）
     */
    public static boolean isSvgFile(String fileName) {
        if (fileName == null) return false;

        String lowerName = fileName.toLowerCase();
        return ALLOWED_EXTENSIONS.stream()
                .anyMatch(lowerName::endsWith);
    }

    /**
     * 基本的SVG文件验证
     */
    private static boolean validateSvgFile(String fileName, byte[] fileData) {
        try {
            // 转换为字符串进行检查
            String content = new String(fileData, StandardCharsets.UTF_8);
            content = content.trim();

            // 检查文件大小
            if (fileData.length == 0) {
                return false;
            }

            // 检查是否包含XML声明和SVG标签
            boolean hasXmlHeader = content.startsWith(SVG_HEADER);
            boolean hasSvgTag = content.contains(SVG_START_TAG);

            // 简单验证：要么有XML声明+SVG标签，要么直接以<svg开头
            if ((hasXmlHeader && hasSvgTag) || content.startsWith(SVG_START_TAG)) {
                return true;
            }

            // 对于压缩的SVGZ文件，尝试解压验证
            if (fileName.toLowerCase().endsWith(".svgz")) {
                // 这里可以添加SVGZ解压验证逻辑
                return true; // 暂时返回true，实际使用时需要解压验证
            }

            return false;

        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 详细的SVG验证
     */
    private ValidationResult validateSvgWithDetails(String fileName, byte[] fileData,
                                                           ValidationOptions options) {
        ValidationResult result = new ValidationResult(fileName);

        try {
            String content = new String(fileData, StandardCharsets.UTF_8).trim();

            // 1. 检查文件大小
            if (fileData.length > options.getMaxFileSize()) {
                result.setError("文件大小超过限制: " + fileData.length + " bytes");
                return result;
            }

            if (fileData.length == 0) {
                result.setError("文件为空");
                return result;
            }

            // 2. 检查基本结构
            boolean hasXmlHeader = content.startsWith(SVG_HEADER);
            boolean hasSvgTag = content.contains(SVG_START_TAG);

            if (!hasSvgTag) {
                result.setError("缺少SVG标签");
                return result;
            }

            // 3. 如果开启了XML验证，进行严格的XML解析
            if (options.isValidateXml()) {
                if (!isValidXml(content)) {
                    result.setError("无效的XML结构");
                    return result;
                }

                // 提取SVG属性
                Map<String, String> attributes = extractSvgAttributes(content);
                result.setSvgAttributes(attributes);

                // 检查SVG尺寸
                if (options.isCheckDimensions()) {
                    String width = attributes.get("width");
                    String height = attributes.get("height");

                    if (width != null && height != null) {
                        try {
                            // 移除单位（px, em, %等）
                            width = width.replaceAll("[^0-9.]", "");
                            height = height.replaceAll("[^0-9.]", "");

                            double w = Double.parseDouble(width);
                            double h = Double.parseDouble(height);

                            if (w > options.getMaxWidth() || h > options.getMaxHeight()) {
                                result.setError(String.format("尺寸过大: %.0fx%.0f", w, h));
                                return result;
                            }

                            if (w < options.getMinWidth() || h < options.getMinHeight()) {
                                result.setError(String.format("尺寸过小: %.0fx%.0f", w, h));
                                return result;
                            }
                        } catch (NumberFormatException e) {
                            // 如果无法解析尺寸，可能是使用百分比等单位
                        }
                    }
                }
            }

            // 4. 检查是否有潜在危险元素（安全考虑）
            if (options.isCheckSecurity()) {
                if (containsDangerousElements(content)) {
                    result.setError("包含潜在危险元素");
                    return result;
                }
            }

            result.setValid(true);

        } catch (Exception e) {
            result.setError("验证异常: " + e.getMessage());
        }

        return result;
    }

    /**
     * 检查是否为有效的XML
     */
    private boolean isValidXml(String content) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // 安全配置，防止XXE攻击
            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            factory.setXIncludeAware(false);
            factory.setExpandEntityReferences(false);

            DocumentBuilder builder = factory.newDocumentBuilder();
            builder.parse(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
            return true;
        } catch (ParserConfigurationException | SAXException | IOException e) {
            return false;
        }
    }

    /**
     * 提取SVG属性
     */
    private Map<String, String> extractSvgAttributes(String content) {
        Map<String, String> attributes = new HashMap<>();

        try {
            int startIndex = content.indexOf("<svg");
            if (startIndex == -1) return attributes;

            int endIndex = content.indexOf('>', startIndex);
            if (endIndex == -1) return attributes;

            String svgTag = content.substring(startIndex, endIndex);

            // 简单解析属性
            String[] parts = svgTag.split("\\s+");
            for (String part : parts) {
                if (part.contains("=")) {
                    String[] keyValue = part.split("=", 2);
                    if (keyValue.length == 2) {
                        String key = keyValue[0].trim();
                        String value = keyValue[1].trim().replaceAll("[\"']", "");
                        attributes.put(key, value);
                    }
                }
            }
        } catch (Exception e) {
            // 解析失败，返回空map
        }

        return attributes;
    }

    /**
     * 检查是否包含危险元素
     */
    private boolean containsDangerousElements(String content) {
        String lowerContent = content.toLowerCase();

        // 检查潜在的脚本标签
        if (lowerContent.contains("<script") ||
                lowerContent.contains("javascript:") ||
                lowerContent.contains("onload=") ||
                lowerContent.contains("onclick=") ||
                lowerContent.contains("xlink:href=")) {
            return true;
        }

        return false;
    }

    /**
     * 获取文件扩展名
     */
    public String getFileExtension(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        return dotIndex > 0 ? fileName.substring(dotIndex + 1).toLowerCase() : "";
    }

    // =============== 内部类和异常 ===============

    /**
     * SVG文件信息类
     */
    public class SvgFileInfo {
        private final String fileName;
        private final InputStream inputStream;
        private final long fileSize;
        private final long lastModified;
        private final Map<String, String> svgAttributes;

        public SvgFileInfo(String fileName, InputStream inputStream, long fileSize,
                           long lastModified, Map<String, String> svgAttributes) {
            this.fileName = fileName;
            this.inputStream = inputStream;
            this.fileSize = fileSize;
            this.lastModified = lastModified;
            this.svgAttributes = svgAttributes;
        }

        // Getters
        public String getFileName() { return fileName; }
        public InputStream getInputStream() { return inputStream; }
        public long getFileSize() { return fileSize; }
        public long getLastModified() { return lastModified; }
        public Map<String, String> getSvgAttributes() { return svgAttributes; }

        public String getBaseName() {
            int dotIndex = fileName.lastIndexOf('.');
            return dotIndex > 0 ? fileName.substring(0, dotIndex) : fileName;
        }

        public String getExtension() {
            return getFileExtension(fileName);
        }
    }

    /**
     * 验证选项类
     */
    public class ValidationOptions {
        private boolean strictMode = true;           // 严格模式，不允许非SVG文件
        private boolean validateXml = true;          // 验证XML结构
        private boolean checkDimensions = true;      // 检查尺寸
        private boolean checkSecurity = true;        // 安全检查
        private long maxFileSize = 10 * 1024 * 1024; // 最大文件大小（10MB）
        private double maxWidth = 5000;              // 最大宽度
        private double maxHeight = 5000;             // 最大高度
        private double minWidth = 10;                // 最小宽度
        private double minHeight = 10;               // 最小高度

        // Getters and Setters
        public boolean isStrictMode() { return strictMode; }
        public void setStrictMode(boolean strictMode) { this.strictMode = strictMode; }

        public boolean isValidateXml() { return validateXml; }
        public void setValidateXml(boolean validateXml) { this.validateXml = validateXml; }

        public boolean isCheckDimensions() { return checkDimensions; }
        public void setCheckDimensions(boolean checkDimensions) { this.checkDimensions = checkDimensions; }

        public boolean isCheckSecurity() { return checkSecurity; }
        public void setCheckSecurity(boolean checkSecurity) { this.checkSecurity = checkSecurity; }

        public long getMaxFileSize() { return maxFileSize; }
        public void setMaxFileSize(long maxFileSize) { this.maxFileSize = maxFileSize; }

        public double getMaxWidth() { return maxWidth; }
        public void setMaxWidth(double maxWidth) { this.maxWidth = maxWidth; }

        public double getMaxHeight() { return maxHeight; }
        public void setMaxHeight(double maxHeight) { this.maxHeight = maxHeight; }

        public double getMinWidth() { return minWidth; }
        public void setMinWidth(double minWidth) { this.minWidth = minWidth; }

        public double getMinHeight() { return minHeight; }
        public void setMinHeight(double minHeight) { this.minHeight = minHeight; }
    }

    /**
     * 验证结果类
     */
    public class ValidationResult {
        private final String fileName;
        private boolean valid;
        private String errorMessage;
        private Map<String, String> svgAttributes = new HashMap<>();

        public ValidationResult(String fileName) {
            this.fileName = fileName;
            this.valid = false;
        }

        // Getters and Setters
        public String getFileName() { return fileName; }
        public boolean isValid() { return valid; }
        public void setValid(boolean valid) { this.valid = valid; }

        public String getErrorMessage() { return errorMessage; }
        public void setError(String errorMessage) {
            this.errorMessage = errorMessage;
            this.valid = false;
        }

        public Map<String, String> getSvgAttributes() { return svgAttributes; }
        public void setSvgAttributes(Map<String, String> svgAttributes) {
            this.svgAttributes = svgAttributes;
        }
    }

    /**
     * 自定义异常类
     */
    public static class InvalidSvgException extends Exception {
        public InvalidSvgException(String message) {
            super(message);
        }

        public InvalidSvgException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}