package com.ruoyi.cc.utils;

import com.ruoyi.cc.domain.FirewallRule;
import com.ruoyi.cc.service.ICcParamsService;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import sun.awt.AppContext;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Service
@Slf4j
public class FirewalldXmlUtil {

    @Autowired
    private  ICcParamsService ccParamsService;
    @Value("${server.port:8899}")
    private Integer serverPort;

    private  String templateFilePath = "";
    private  String getFirewallTemplateFilePath(){
        if(StringUtil.isNullOrEmpty(templateFilePath)) {
            String fsConfDir = ccParamsService.getParamValueByCode("fs_conf_directory", "");
            templateFilePath = String.format("%s%s", fsConfDir, "/autoload_configs/firewalld-template.xml");
        }
        return templateFilePath;
    }

    public  List<FirewallRule> getAllRules() throws Exception {

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

        Document doc = load();

        Element root = doc.getDocumentElement(); // <zone>

        // 遍历 zone 的直接子节点
        NodeList children = root.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node node = children.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element elem = (Element) node;
                String tagName = elem.getTagName();

                if ("port".equals(tagName)) {
                    // 全局端口（直接子节点）
                    FirewallRule rule = parsePortElement(elem, null);
                    if (rule != null) {

                        rule.setAllowEdit(1);
                        if (serverPort.equals(rule.getPortStart()) && serverPort.equals(rule.getPortEnd())) {
                            rule.setAllowEdit(0);
                        }
                        rule.setId(rule.getProtocol() + "-" + rule.getPortStart() + "-" + rule.getPortEnd());

                        list.add(rule);
                    }
                } else if ("rule".equals(tagName)) {
                    // rule 内的端口
                    FirewallRule rule = parseRuleElement(elem);
                    if (rule != null) {

                        rule.setAllowEdit(1);
                        if (serverPort.equals(rule.getPortStart()) && serverPort.equals(rule.getPortEnd())) {
                            rule.setAllowEdit(0);
                        }
                        rule.setId(rule.getProtocol() + "-" + rule.getPortStart() + "-" + rule.getPortEnd()+ "-" + rule.getFromSource());
                        list.add(rule);
                    }
                }
            }
        }
        return list;
    }

    private static FirewallRule parsePortElement(Element portElem, String sourceAddress) {
        String portStr = portElem.getAttribute("port");
        String protocol = portElem.getAttribute("protocol");

        if (portStr.isEmpty() || protocol.isEmpty()) {
            return null;
        }

        FirewallRule rule = new FirewallRule();
        rule.setId(UUID.randomUUID().toString());
        rule.setProtocol(protocol);

        // 解析端口范围
        parsePortRange(rule, portStr);

        // 设置source相关属性
        if (sourceAddress != null && !sourceAddress.isEmpty()) {
            rule.setEnableFromSource(true);
            rule.setFromSource(sourceAddress);
        } else {
            rule.setEnableFromSource(false);
            rule.setFromSource(null);
        }

        return rule;
    }

    private static FirewallRule parseRuleElement(Element ruleElem) {
        // 获取source地址
        NodeList sourceNodes = ruleElem.getElementsByTagName("source");
        String sourceAddress = null;
        if (sourceNodes.getLength() > 0) {
            Element sourceElem = (Element) sourceNodes.item(0);
            sourceAddress = sourceElem.getAttribute("address");
        }

        // 只获取当前 rule 下的 port（不是全局的）
        NodeList portNodes = ruleElem.getElementsByTagName("port");
        if (portNodes.getLength() > 0) {
            Element portElem = (Element) portNodes.item(0);
            return parsePortElement(portElem, sourceAddress);
        }

        return null;
    }

    private static void parsePortRange(FirewallRule rule, String portStr) {
        if (portStr.contains("-")) {
            String[] parts = portStr.split("-");
            rule.setPortStart(Integer.parseInt(parts[0].trim()));
            rule.setPortEnd(Integer.parseInt(parts[1].trim()));
        } else {
            int port = Integer.parseInt(portStr.trim());
            rule.setPortStart(port);
            rule.setPortEnd(port);
        }
    }

    public  boolean addRule(FirewallRule rule) throws Exception {

        Document doc = load();

        Element root = doc.getDocumentElement();

        if (rule.isEnableFromSource()) {

            Element ruleNode = doc.createElement("rule");
            ruleNode.setAttribute("family", "ipv4");

            Element source = doc.createElement("source");
            source.setAttribute("address", rule.getFromSource());

            Element port = doc.createElement("port");
            port.setAttribute("protocol", rule.getProtocol());

            if (rule.getPortStart().equals(rule.getPortEnd())) {
                port.setAttribute("port", String.valueOf(rule.getPortStart()));
            } else {
                port.setAttribute("port",
                        rule.getPortStart() + "-" + rule.getPortEnd());
            }

            Element accept = doc.createElement("accept");

            ruleNode.appendChild(source);
            ruleNode.appendChild(port);
            ruleNode.appendChild(accept);

            root.appendChild(ruleNode);

        } else {

            Element port = doc.createElement("port");

            port.setAttribute("protocol", rule.getProtocol());

            if (rule.getPortStart().equals(rule.getPortEnd())) {
                port.setAttribute("port", String.valueOf(rule.getPortStart()));
            } else {
                port.setAttribute("port",
                        rule.getPortStart() + "-" + rule.getPortEnd());
            }

            root.appendChild(port);
        }

        save(doc);
        return true;
    }

    public  boolean delete(FirewallRule ruleParam) throws Exception {
        String protocol = ruleParam.getProtocol();
        int portStart = ruleParam.getPortStart();
        int portEnd = ruleParam.getPortEnd();
        String sourceIp = ruleParam.getFromSource();

        Document doc = load();

        String targetPort;

        if (portStart == portEnd) {
            targetPort = String.valueOf(portStart);
        } else {
            targetPort = portStart + "-" + portEnd;
        }

        Element root = doc.getDocumentElement();

        // 删除普通 <port> 规则
        if (sourceIp == null || sourceIp.trim().isEmpty()) {

            NodeList ports = doc.getElementsByTagName("port");

            for (int i = ports.getLength() - 1; i >= 0; i--) {

                Element port = (Element) ports.item(i);

                if (!port.getParentNode().equals(root)) {
                    continue;
                }

                String proto = port.getAttribute("protocol");
                String portValue = port.getAttribute("port");

                if (protocol.equals(proto) && targetPort.equals(portValue)) {

                    root.removeChild(port);
                }
            }

        } else {

            // 删除 <rule> 规则
            NodeList rules = doc.getElementsByTagName("rule");

            for (int i = rules.getLength() - 1; i >= 0; i--) {

                Element rule = (Element) rules.item(i);

                NodeList sources = rule.getElementsByTagName("source");
                NodeList ports = rule.getElementsByTagName("port");

                if (sources.getLength() == 0 || ports.getLength() == 0) {
                    continue;
                }

                Element source = (Element) sources.item(0);
                Element port = (Element) ports.item(0);

                String address = source.getAttribute("address");
                String proto = port.getAttribute("protocol");
                String portValue = port.getAttribute("port");

                if (sourceIp.equals(address)
                        && protocol.equals(proto)
                        && targetPort.equals(portValue)) {

                    root.removeChild(rule);
                }
            }
        }

        save(doc);
        return true;
    }

    private  Document load() throws Exception {

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        DocumentBuilder builder = factory.newDocumentBuilder();

        return builder.parse(new File(getFirewallTemplateFilePath()));
    }

    private  boolean save(Document doc) throws Exception {

        TransformerFactory factory = TransformerFactory.newInstance();

        Transformer transformer = factory.newTransformer();

        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        DOMSource source = new DOMSource(doc);

        StreamResult result = new StreamResult(new File(getFirewallTemplateFilePath()));

        transformer.transform(source, result);

        return false;
    }
}