package org.example.Utils;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.function.Function;

public class XmlUtil {
    private static final Charset DEFAULT_ENCODING = Charset.forName("GB2312");

    /**
     * 将对象序列化为XML字符串
     *
     * @param obj      要序列化的对象
     * @param encoding 编码方式
     * @return 序列化后的XML字符串
     * @throws JAXBException JAXB处理异常
     * @throws IOException   IO异常
     */
    public static String xmlSerialize(Object obj, Charset encoding) throws JAXBException, IOException {
        if (obj == null) {
            throw new IllegalArgumentException("对象不能为空");
        }
        Charset useEncoding = (encoding != null) ? encoding : DEFAULT_ENCODING;

        JAXBContext context = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = context.createMarshaller();
        // 格式化输出
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        // 设置编码
        marshaller.setProperty(Marshaller.JAXB_ENCODING, useEncoding.name());
        // 去除默认XML声明（如需保留可删除此行）
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

        StringWriter writer = new StringWriter();
        // 手动写入XML声明（因为关闭了JAXB_FRAGMENT）
        writer.write("<?xml version=\"1.0\" encoding=\"" + useEncoding.name() + "\"?>\n");
        marshaller.marshal(obj, writer);
        return writer.toString();
    }

    /**
     * 将对象序列化为XML并写入文件
     *
     * @param obj      要序列化的对象
     * @param path     文件路径
     * @param header   XML头部（可为null）
     * @param encoding 编码方式
     * @throws JAXBException JAXB处理异常
     * @throws IOException   IO异常
     */
    public static void xmlSerializeToFile(Object obj, String path, String header, Charset encoding) throws JAXBException, IOException {
        if (path == null || path.isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        Charset useEncoding = (encoding != null) ? encoding : DEFAULT_ENCODING;

        File file = new File(path);
        // 处理文件不存在的情况
        if (!file.exists()) {
            File parentDir = file.getParentFile();
            if (parentDir != null && !parentDir.exists()) {
                parentDir.mkdirs();
            }
            // 写入默认头部
            String defaultHeader = "<?xml version='1.0' encoding='gb2312'?>\n" +
                    "<Config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>\n</Config>";
            String useHeader = (header != null && !header.isEmpty()) ? header : defaultHeader;
            Files.write(Paths.get(path), useHeader.getBytes(useEncoding));
        }

        // 序列化对象到文件
        JAXBContext context = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, useEncoding.name());
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

        // 关键修改：将 XMLStreamWriter 声明为 AutoCloseable
        try (FileOutputStream fos = new FileOutputStream(file);
             OutputStreamWriter osw = new OutputStreamWriter(fos, useEncoding);
             AutoCloseable xmlWriter = (AutoCloseable) XMLOutputFactory.newInstance().createXMLStreamWriter(osw)) {

            // 强制转换为 XMLStreamWriter 使用其方法
            XMLStreamWriter writer = (XMLStreamWriter) xmlWriter;
            // 写入XML声明
            writer.writeStartDocument(useEncoding.name(), "1.0");
            writer.writeCharacters("\n");
            marshaller.marshal(obj, writer);
            writer.writeEndDocument();
        } catch (XMLStreamException | ClassCastException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从XML字符串反序列化对象
     *
     * @param xml      XML字符串
     * @param clazz    目标类型
     * @param encoding 编码方式
     * @param <T>      目标类型泛型
     * @return 反序列化后的对象
     * @throws JAXBException JAXB处理异常
     */
    public static <T> T xmlDeserialize(String xml, Class<T> clazz, Charset encoding) throws JAXBException {
        if (xml == null || xml.isEmpty()) {
            throw new IllegalArgumentException("XML字符串不能为空");
        }
        Charset useEncoding = (encoding != null) ? encoding : DEFAULT_ENCODING;

        JAXBContext context = JAXBContext.newInstance(clazz);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        try (InputStream is = new ByteArrayInputStream(xml.getBytes(useEncoding))) {
            return clazz.cast(unmarshaller.unmarshal(is));
        } catch (IOException e) {
            throw new JAXBException("反序列化输入流处理失败", e);
        }
    }

    /**
     * 从文件反序列化对象
     *
     * @param path     文件路径
     * @param clazz    目标类型
     * @param encoding 编码方式
     * @param <T>      目标类型泛型
     * @return 反序列化后的对象
     * @throws JAXBException JAXB处理异常
     * @throws IOException   IO异常
     */
    public static <T> T xmlDeserializeFromFile(String path, Class<T> clazz, Charset encoding) throws JAXBException, IOException {
        if (path == null || path.isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        File file = new File(path);
        if (!file.exists()) {
            return null;
        }
        Charset useEncoding = (encoding != null) ? encoding : DEFAULT_ENCODING;

        String xml = new String(Files.readAllBytes(Paths.get(path)), useEncoding);
        return xml.isEmpty() ? null : xmlDeserialize(xml, clazz, useEncoding);
    }

    /**
     * 从文件反序列化对象（带预处理函数）
     *
     * @param path              文件路径
     * @param type              目标类型
     * @param encoding          编码方式
     * @param beforeDeserialize 反序列化前的XML预处理函数
     * @return 反序列化后的对象
     * @throws JAXBException JAXB处理异常
     * @throws IOException   IO异常
     */
    public static Object xmlDeserializeFromFile(String path, Class<?> type, Charset encoding,
                                                Function<String, String> beforeDeserialize) throws JAXBException, IOException {
        if (path == null || path.isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        File file = new File(path);
        if (!file.exists()) {
            return null;
        }
        Charset useEncoding = (encoding != null) ? encoding : DEFAULT_ENCODING;

        String xml = new String(Files.readAllBytes(Paths.get(path)), useEncoding);
        if (xml.isEmpty()) {
            return null;
        }
        // 应用预处理函数
        if (beforeDeserialize != null) {
            xml = beforeDeserialize.apply(xml);
        }
        return xmlDeserialize(xml, type, useEncoding);
    }
}