package com.mortals.xhx.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.alibaba.fastjson.JSON;
import com.mortals.xhx.common.pdu.NodeEntity;
import lombok.SneakyThrows;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.util.ObjectUtils;
import org.springframework.util.xml.SimpleNamespaceContext;
import org.w3c.dom.*;

import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPathConstants;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author: zxfei
 * @date: 2021/10/16 17:00
 * @description:
 **/
@CommonsLog
public class ExportDocUtil {

    //针对单个标签id 返回路径
    public static List<String> getIdsByTagName(String tagName, List<NodeEntity> list) {
        return list.stream().filter(f -> tagName.equals(f.getTagName())).map(i -> i.getId()).collect(Collectors.toList());

    }

    public static Document parseTemplateImage(List<String> paths, String el, Document document) {
        NamespaceContext ns = ExportDocUtil.buildNsByDocument(document);
        paths = CollUtil.reverse(paths);
        String xpath = "/" + paths.stream().collect(Collectors.joining("/"));
        Node node = (Node) XmlUtil.getByXPath(xpath, document, XPathConstants.NODE, ns);
        node.setTextContent(el);
        return document;
    }


    public static Document replaceTemplateEl(List<String> paths, String targetEl, String sourceEl, Document document) {
        NamespaceContext ns = ExportDocUtil.buildNsByDocument(document);
        paths = CollUtil.reverse(paths);
        String xpath = "/" + paths.stream().collect(Collectors.joining("/"));

        Node node = ((Node) XmlUtil.getByXPath(xpath, document, XPathConstants.NODE, ns));
        if (!ObjectUtils.isEmpty(node) && sourceEl.equals(node.getTextContent())) {
            node.setTextContent(targetEl);
        }
//        NodeList nodeList = ((NodeList) XmlUtil.getByXPath(xpath, document, XPathConstants.NODESET, ns));
//        for (int i = 0; i < nodeList.getLength(); i++) {
//            Node node = nodeList.item(i);
//            if (sourceEl.equals(node.getTextContent())) {
//                node.setTextContent(targetEl);
//            }
//        }
        return document;
    }


    public static NodeList getNodeListByPath(List<String> paths, Document document) {
        NamespaceContext ns = ExportDocUtil.buildNsByDocument(document);
        paths = CollUtil.reverse(paths);
        String xpath = "/" + paths.stream().collect(Collectors.joining("/"));
        //log.info("xpath" + xpath);
        NodeList nodeList = ((NodeList) XmlUtil.getByXPath(xpath, document, XPathConstants.NODESET, ns));
        return nodeList;
    }

    public static Node getNodeByPath(List<String> paths, Document document) {
        NamespaceContext ns = ExportDocUtil.buildNsByDocument(document);
        paths = CollUtil.reverse(paths);
        String xpath = "/" + paths.stream().collect(Collectors.joining("/"));
        //log.info("xpath" + xpath);
        Node node = ((Node) XmlUtil.getByXPath(xpath, document, XPathConstants.NODE, ns));
        return node;
    }

    public static Node getNodeByPathStr(String xpath, Document document) {
        NamespaceContext ns = ExportDocUtil.buildNsByDocument(document);

        Node node = ((Node) XmlUtil.getByXPath(xpath, document, XPathConstants.NODE, ns));
        return node;
    }

    //(NodeList)

    public static NodeEntity getElementMapById(String id, List<NodeEntity> list) {
        for (NodeEntity node : list) {
            if (id.equals(node.getId())) {
                return node;
            }
        }
        return null;
    }

    /**
     * 递归解析XML 文件的 把标签名 和 属性值 存入到 list里面，并且每一个map里面存入 id 和 parent_id 来记录 map直接的上下级关系
     *
     * @param list 生成的一个带有 id 和 parent_id 的list
     * @param path 文件的绝对路径，目前只支持绝对路径
     */
    public static Document parseXMLFile(List<NodeEntity> list, String path) {
        // 读XML文件
        Document document = XmlUtil.readXML(new File(path));
        Element element = document.getDocumentElement();
        // 第一次调用，就是第一级，parent_id 就给空
        parseElement(list, element, "", 1);

        return document;
    }

    public static Document parseXMLStr(List<NodeEntity> list, String xmlStr) {

        Document document = XmlUtil.readXML(xmlStr);
        Element element = document.getDocumentElement();
        // 第一次调用，就是第一级，parent_id 就给空
        parseElement(list, element, "", 1);

        return document;
    }

    public static Document parseDocument(List<NodeEntity> list, Document document) {
        Element element = document.getDocumentElement();
        // 第一次调用，就是第一级，parent_id 就给空
        parseElement(list, element, "", 1);
        return document;
    }

    /**
     * 递归解析 XML 文件
     */
    private static void parseElement(List<NodeEntity> list, Element element, String parentId, Integer num) {
        // 设置id来记录上下级关系
        String id = IdUtil.fastSimpleUUID();

        NodeEntity nodeEntity = new NodeEntity();

        Map<String, String> elementMap = new HashMap();
        // 拿到当前element的所有的属性名和属性值
        NamedNodeMap namedNodeMap = element.getAttributes();
        // 遍历复制给firstMap存起来
        for (int i = 0; i < namedNodeMap.getLength(); i++) {
            Attr attr = (Attr) namedNodeMap.item(i);
            elementMap.put(attr.getName(), attr.getValue());
        }
        // 把标签名也记录下来
        nodeEntity.setTagName(element.getTagName());
        nodeEntity.setNodeType(element.getNodeType() + "");
        nodeEntity.setId(id);
        nodeEntity.setParentId(parentId);
        nodeEntity.setNum(num);
//        elementMap.put("TagName", element.getTagName());
//        elementMap.put("NodeType", element.getNodeType() + "");

        // 把 id 放到 map 里面
//        elementMap.put("id", id);
//        elementMap.put("parentId", parentId);
        list.add(nodeEntity);
//        if ("w:binData".equals(element.getTagName())) {
//            System.out.println("content:" + element.getTextContent());
//            element.setTextContent("${image1}");
//        }

        // 拿到当前element的子节点集合
        NodeList nodeList = element.getChildNodes();
        Node childNode;


        // 有可能没有子节点,做一个非空判断
        if (nodeList.getLength() != 0) {
            //分组子节点分组编号，且是element节点
            Map<String, Integer> numMap = new HashMap<>();
            // 遍历当前element的所有的子节点
            for (int i = 0; i < nodeList.getLength(); i++) {
                childNode = nodeList.item(i);
                if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                    Integer numNew = 1;
                    if (numMap.containsKey(childNode.getNodeName())) {
                        numNew = MapUtil.getInt(numMap, childNode.getNodeName()) + 1;
                        // numNew = numMap.computeIfPresent(childNode.getNodeName(), (key, value) -> value+1);
                    }
                    numMap.put(childNode.getNodeName(), numNew);
                    //log.info("numNew"+numNew);
                    parseElement(list, (Element) childNode, id, numNew);
                } else if (childNode.getNodeType() == Node.TEXT_NODE) {
                    //构建叶子文本节点
                    //log.info("NodeText:" + childNode.getTextContent());
                    //log.info("Node:" + JSON.toJSONString(childNode));

                    String leafId = IdUtil.fastSimpleUUID();
                    NodeEntity leafNode = new NodeEntity();

                    String text = childNode.getTextContent();
                    //String text = "";
                    String regEx = "\\$\\{.+?\\}";
                    Pattern pattern = Pattern.compile(regEx);
                    List<String> allGroups = ReUtil.getAllGroups(pattern, childNode.getTextContent());
                    if (!ObjectUtils.isEmpty(allGroups)) {
                        // text = CollUtil.join(allGroups, ";");
                        text = Arrays.asList(childNode.getTextContent().split("\\$")).stream().map(String::trim)
                                .map(item -> StrUtil.subBetween(item, "{", "}"))
                                .filter(f -> f != null)
                                .map(item -> "${" + item + "}")
                                .collect(Collectors.joining(";"));
                    }

                    leafNode.setId(leafId);
                    leafNode.setContent(text);
                    leafNode.setParentId(id);
                    leafNode.setNodeType(childNode.getNodeType() + "");
                    list.add(leafNode);
                }
            }
        }
    }

    public static NamespaceContext buildNsByDocument(Document document) {
        SimpleNamespaceContext ns = new SimpleNamespaceContext();
        Element rootElement = XmlUtil.getRootElement(document);
        NamedNodeMap namedNodeMap = rootElement.getAttributes();
        // 遍历复制给firstMap存起来
        for (int i = 0; i < namedNodeMap.getLength(); i++) {
            Attr attr = (Attr) namedNodeMap.item(i);
            if (attr.getName().startsWith("xmlns:")) {
                ns.bindNamespaceUri(attr.getName().split(":")[1], attr.getValue());
            }
        }
        return ns;
    }


    public static List<String> getPath(List<String> paths, List<NodeEntity> list, String parentId) {
        if ("".equals(parentId)) {
            return paths;
        } else {
            for (NodeEntity node : list) {
                if (parentId.equals(node.getId())) {
                    StringBuilder path = new StringBuilder();
                    path.append(node.getTagName());
                    if (node.getNum() != null) {
                        path.append("[");
                        path.append(node.getNum());
                        path.append("]");
                    }
                    paths.add(path.toString());
                    parentId = node.getParentId();
                    break;
                }
            }
            return getPath(paths, list, parentId);
        }
    }


    public static String createCheckBoxStr(Boolean checked, String label) {
        StringBuilder sb = new StringBuilder();
        if (checked) {
            String ckHeader = "<w:r>" +
                    "<w:rPr>" +
                    "<w:rFonts w:ascii=\"楷体\" w:h-ansi=\"楷体\"/>" +
                    "<wx:font wx:val=\"楷体\"/>" +
                    "</w:rPr>" +
                    "<w:sym w:font=\"Wingdings 2\" w:char=\"0052\"/>" +
                    "</w:r>";

            sb.append(ckHeader);
        } else {
            String ckHeader = "<w:r>" +
                    "<w:rPr>" +
                    "<w:rFonts w:ascii=\"楷体\" w:h-ansi=\"楷体\"/>" +
                    "<wx:font wx:val=\"楷体\"/>" +
                    "</w:rPr>" +
                    "<w:t>□</w:t>" +
                    "</w:r>";
            sb.append(ckHeader);
        }

        String ckBody = "<w:r>" +
                "<w:rPr>" +
                "<w:rFonts w:ascii=\"楷体\" w:h-ansi=\"楷体\"/>" +
                "<wx:font wx:val=\"楷体\"/>\n" +
                "</w:rPr>" +
                "<w:t>" + label + "</w:t>" +
                "</w:r>";
        sb.append(ckBody);
        //return "111";
        return sb.toString();
    }


    public static String createCheckBoxStrOther(Boolean checked, String label) {
        StringBuilder sb = new StringBuilder();
        if (checked) {
            String ckHeader = "☑";

            sb.append(ckHeader);
        } else {
            String ckHeader = "□";
            sb.append(ckHeader);
        }
        sb.append(label);
        return sb.toString();
    }

    /**
     * 校验并更新文档中的非法el表达式
     *
     * @param document
     * @param list
     */
    public static void checkAndUpdateExpressionByDocument(Document document, List<NodeEntity> list) {
        //查找存在非法的el表达式 并找到当前节点
        List<NodeEntity> checkedList = list.stream().filter(f -> f.getContent() != null).filter(f -> !"".equals(f.getContent())).map(item -> {
            String content = item.getContent();
            //log.info("content" + content);
            //判断当前的文本值是否$开始 但是没有{}结尾，如$ ,${ ,$}等
            if (StrUtil.startWith(content, "$")) {
                //log.info("content="+content);
                //只要结束符不是} 开始不是{d  都返回错误
                if (StrUtil.startWith(content, "${") && StrUtil.endWith(content, "}")) {
                    //包含$  未包含{ 或 }的 返回
                    return null;
                } else {
                    //log.info("??????"+content);
                    return item;
                }
            }
            return null;
        }).filter(f -> f != null).collect(Collectors.toList());

        //遍历非法的节点，
        List<String> deletePathList = new ArrayList<>();
        for (NodeEntity node : checkedList) {
            String parentId = node.getParentId();
            List<String> paths = new ArrayList<>();
            //paths.add(tagName);
            paths.add("text()");
            ExportDocUtil.getPath(paths, list, parentId);
            Node nodeXml = ExportDocUtil.getNodeByPath(paths, document);
            log.info("bad el expression" + nodeXml.getTextContent().trim());
            if (!ObjectUtils.isEmpty(nodeXml) && nodeXml.getTextContent().trim().indexOf("$") != -1) {
                StringBuilder elNew = new StringBuilder();
                elNew.append(nodeXml.getTextContent().trim());
                //查找当前行节点节点兄弟节点 并查询到结尾符为"}" 并记录 合并后替换当前节点
                //修改父标签[num]值 xpath查找文本值，如果不为空 则找到，然后累加找到结束符"}"
                //paths = CollUtil.reverse(paths);
                List<String> tempList = new ArrayList<>();
                CollUtil.addAll(tempList, paths);
                for (int i = tempList.size() - 1; i > 0; i--) {
                    String path = tempList.get(i);
                    String num = StrUtil.subBetween(path, "[", "]");
                    Integer temp = 1;
                    String tag = StrUtil.subBefore(path, "[", false);
                    if (!ObjectUtils.isEmpty(num)) {
                        temp = Integer.parseInt(num) + 1;
                        tempList.set(i, tag + "[" + temp + "]");
                    } else {
                        continue;
                    }

                    String xpath = "/" + tempList.stream().collect(Collectors.joining("/"));
                    //log.info("xpath" + xpath);
                    Node tempnodeXml = ExportDocUtil.getNodeByPathStr(xpath, document);
                    if (!ObjectUtils.isEmpty(tempnodeXml)) {
                        if (!"".equals(tempnodeXml.getTextContent().trim())) {
                            //log.info("textContent:" + tempnodeXml.getTextContent().trim());
                            //保存当前节点xpth,后续查找后删除
                            List<String> tempPathList = new ArrayList<>();
                            for (int j = 0; j <= i; j++) {
                                tempPathList.add(tempList.get(j));
                            }

                            String xpath1 = "/" + tempPathList.stream().collect(Collectors.joining("/"));
                            //log.info("xpath1" + xpath1);
                            deletePathList.add(xpath1);

                            elNew.append(tempnodeXml.getTextContent().trim());
                            for (int j = 1; j < 10; j++) {
                                temp = temp + 1;
                                tempList.set(i, tag + "[" + temp + "]");
                                String xpathTemp = "/" + tempList.stream().collect(Collectors.joining("/"));
                                log.info("xpath" + xpathTemp);
                                Node tempnodeXml1 = ExportDocUtil.getNodeByPathStr(xpathTemp, document);
                                if (!ObjectUtils.isEmpty(tempnodeXml1)) {
                                    tempPathList = new ArrayList<>();
                                    for (int k = 0; k <= i; k++) {
                                        tempPathList.add(tempList.get(k));
                                    }

                                    xpath1 = "/" + tempPathList.stream().collect(Collectors.joining("/"));
                                    log.info("xpath1" + xpath1);
                                    deletePathList.add(xpath1);
                                    elNew.append(tempnodeXml1.getTextContent().trim());
                                    if (tempnodeXml1.getTextContent().trim().endsWith("}")) {
                                        log.info("end }");
                                        break;
                                    }
                                }
                            }
                            break;
                        }
                    }
                    tempList = new ArrayList<>();
                    CollUtil.addAll(tempList, paths);
                }

                nodeXml.setTextContent(elNew.toString());
            }
        }

        for (int i = deletePathList.size() - 1; i >= 0; i--) {
            Node nodeTemp = ExportDocUtil.getNodeByPathStr(deletePathList.get(i), document);
            if (!ObjectUtils.isEmpty(nodeTemp)) {
                nodeTemp.getParentNode().removeChild(nodeTemp);
            }
        }
        //log.info("deletePathList:" + JSON.toJSONString(deletePathList));
    }


    public static void testClearXml() {
        File file = new File("E:\\评审报告(竞争性谈判).xml");

        Document document = XmlUtil.readXML(file);

        List<NodeEntity> list = new ArrayList<>();

        ExportDocUtil.parseXMLFile(list, "E:\\评审报告(竞争性谈判).xml");

        ExportDocUtil.checkAndUpdateExpressionByDocument(document, list);


        XmlUtil.toFile(document, "E:\\评审报告(竞争性谈判)修正.xml");

    }


    @SneakyThrows
    public static void main(String[] args) {

        //测试清洗数据
        ExportDocUtil.testClearXml();

        //解析xml文档，修改非法的$表达式为合法
/*
        File file = new File("E:\\pic\\doc\\建设工程消防验收备案表（模板）修正.xml");

        Document document = XmlUtil.readXML(file);

        List<NodeEntity> list = new ArrayList<>();

        ExportDocUtil.parseXMLFile(list, "E:\\pic\\doc\\建设工程消防验收备案表（模板）修正.xml");

        //查找${col 节点
        NodeEntity colNode = null;
        for (NodeEntity node : list) {
            if (!ObjectUtils.isEmpty(node.getContent())&&node.getContent().startsWith("${col.")) {
                colNode = node;
                break;
            }
        }


        log.info("colNode:" + JSON.toJSONString(colNode));
*/


        //System.out.println(XmlUtil.format(document));


//        Map root = new HashMap();
//        root.put(
//                "doc",
//                freemarker.ext.dom.NodeModel.parse(new File("the/path/of/the.xml")));
//
//
//        String ckStr="    <#list><w:r>\n" +
//                "\t\t\t\t\t\t\t\t\t\t<w:rPr>\n" +
//                "\t\t\t\t\t\t\t\t\t\t\t<w:rFonts w:ascii=\"宋体\" w:h-ansi=\"宋体\"/>\n" +
//                "\t\t\t\t\t\t\t\t\t\t\t<wx:font wx:val=\"宋体\"/>\n" +
//                "\t\t\t\t\t\t\t\t\t\t</w:rPr>\n" +
//                "\t\t\t\t\t\t\t\t\t\t<w:sym w:font=\"Wingdings 2\" w:char=\"0052\"/>\n" +
//                "\t\t\t\t\t\t\t\t\t</w:r></#list>";
//
//        XmlUtil.parseXml(ckStr);

        //<#list columns as column>

//        Document xml = XmlUtil.createXml();
//        Element element = xml.createElement("#list");
//        xml.appendChild(element);
//        Element elementPr = xml.createElement("w:rPr");
//        element.appendChild(elementPr);
//
//        Element elementFonts = xml.createElement("w:rFonts");
//        elementFonts.setAttribute("w:ascii","宋体");
//        elementFonts.setAttribute("w:h-ansi","宋体");
//        elementPr.appendChild(elementFonts);
//        Element elementFont = xml.createElement("wx:font");
//        elementFont.setAttribute("wx:val","宋体");
//        elementPr.appendChild(elementFont);


        // System.out.println(ExportDocUtil.getStringFromDocument(xml));

        // System.out.println(XmlUtil.format(xml));


        // System.out.println(XmlUtil.toStr(element,"utf-8",true,false));


//        String imagepath="E:\\pic\\1.png";
//
//        BufferedImage image = ImgUtil.read(imagepath);
//
//        String png = ImgUtil.toBase64(image, "png");
//
//        System.out.println(png);

//
//        String path = "E:\\新建文件夹\\测试.doc";
//
////        String path = "E:\\pic\\doc\\附件1-四川省危险废物经营许可证申请表（补办）空白.xml";
////
//        List<Map<String, String>> list = new ArrayList<>();
//
//        ExportDocUtil.parseXMLFile(list, path);
//
//        //查找w:binData的节点路径
//
//        //1.
//        String id = "";
//        List<String> paths = new ArrayList<>();
//        for (Map<String, String> map : list) {
//            if ("w:binData".equals(map.get("TagName"))) {
//                System.out.println("id:" + map.get("id"));
//                System.out.println("parentId:" + map.get("parentId"));
//                id = map.get("parentId");
//                paths.add(map.get("TagName"));
//                break;
//            }
//        }
//        ExportDocUtil.getPath(paths, list, id);
//        System.out.println(JSON.toJSONString(paths));
//        Document document = XmlUtil.readXML(new File(path));
//        NamespaceContext ns = ExportDocUtil.buildNsByDocument(document);
//        paths = CollUtil.reverse(paths);
//
//        String xpath = "/" + paths.stream().collect(Collectors.joining("/"));
//
//        System.out.println("xpath:" + xpath);
//
//        Node byXPath = (Node) XmlUtil.getByXPath(xpath, document, XPathConstants.NODE, ns);
//        byXPath.setTextContent("${image1}");
//
//        System.out.println(XmlUtil.toStr(document));
        //System.out.println("xPath"+byXPath.setTextContent(););

        // xpath="//*[name()='wordml://02000001.jpg']";

        // document.getElementById()


        //List<Element> nodeList = document.selectNodes(xpath).selectNodes("//w:tbl//w:tr//w:tc//w:p//w:r//w:t");

        //DefaultNamespaceContext defaultNamespaceContext = DefaultNamespaceContext.create(document);
        //通过dom xpath 获取节点值 并修改
        //Element elementByXPath = XmlUtil.getRootElement(document).getElementsByTagName(document., "w:binData");

        //System.out.println("xpath element:"+elementByXPath.getTextContent());


        //System.out.println(JSON.toJSONString(list));


//        File file = new File("E:\\pic\\doc\\doc.xml");
//
//        Document document = XmlUtil.readXML(file);
//
//        Element element = document.getDocumentElement();
//
//        // 第一次调用，就是第一级，parent_id 就给空
//        ExportDocUtil.parseElement(list,element,"");
//
//        NodeList childNodes = document.getChildNodes();
//        System.out.println("size:"+childNodes.getLength());
//        for(int i=0;i<childNodes.getLength();i++){
//            Node item = childNodes.item(i);
//
//            if(!"".equals(item.getTextContent().trim())){
//                //System.out.println(item.getNodeValue());
//
//                System.out.println(item.getTextContent().trim());
//            }
//        }
    }

}
