package kz.arta.nca_iiscon.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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.ByteArrayInputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;

@Slf4j
public class XmlToJsonUtil {

    static ApplicationIdGenerator gen = new ApplicationIdGenerator();
    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * Извлекает XML из поля data и конвертирует в JSON
     */
    public static String extractDataAsJson(Object response, Boolean isReference, String base64photo, String applicantNumber) {
        try {
                String xmlResponse = (String) response;
                log.info("Processing String XML response");
                // Извлекаем data element из SOAP response
                String xmlString = extractDataContentFromSoapXml(xmlResponse);

                // Конвертируем в JSON с правильным значением isReference
                return xmlToJson(xmlString, isReference, base64photo, applicantNumber);
        } catch (Exception e) {
            log.error("Error extracting data as JSON", e);
            return "{\"error\": \"Failed to parse response\"}";
        }
    }

    /**
     * Извлекает data element из SOAP XML response
     */
    private static String extractDataContentFromSoapXml(String soapXml) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();

        ByteArrayInputStream input = new ByteArrayInputStream(soapXml.getBytes(StandardCharsets.UTF_8));
        Document doc = builder.parse(input);

        // Находим элемент data в SOAP response
        NodeList dataElements = doc.getElementsByTagName("data");
        if (dataElements.getLength() == 0) {
            log.warn("No data element found in SOAP response");
            return "{}";
        }

        // Получаем текстовое содержимое data элемента
        org.w3c.dom.Element dataElement = (org.w3c.dom.Element) dataElements.item(0);
        String xmlContent = getElementTextContent(dataElement);

        log.info("Extracted XML from SOAP response: {}", xmlContent);

        // Возвращаем XML content без конвертации в JSON
        return xmlContent;
    }

    /**
     * Получает текстовое XML содержимое элемента
     */
    private static String getElementTextContent(org.w3c.dom.Element element) {
        // Если внутри data есть XML как текст, возвращаем его
        String textContent = element.getTextContent();
        if (textContent != null && !textContent.trim().isEmpty()) {
            return textContent;
        }

        // Иначе сериализуем дочерние элементы
        try {
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            StringWriter writer = new StringWriter();

            NodeList children = element.getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                transformer.transform(new DOMSource(children.item(i)), new StreamResult(writer));
            }

            return writer.toString();
        } catch (Exception e) {
            log.error("Failed to serialize element content", e);
            return "";
        }
    }


    /**
     * Конвертирует XML строку в JSON
     */
    public static String xmlToJson(String xmlString, Boolean isReference, String base64photo, String applicantNumber) {
        try {
            // Парсим XML
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();

            ByteArrayInputStream input = new ByteArrayInputStream(
                    xmlString.getBytes(StandardCharsets.UTF_8)
            );
            Document doc = builder.parse(input);

            // Конвертируем в JSON
            ObjectNode jsonNode = objectMapper.createObjectNode();
            Element root = doc.getDocumentElement();

            parseElement(root, jsonNode);

            if (isReference) {
                String applicationId = gen.generate();
                String digiSign = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><application><applicationId>" + applicationId + "</applicationId><serviceTypeCode>C09-03</serviceTypeCode><registerOrgCode>MID-GER-MUNCHEN</registerOrgCode><executorOrgCode>MTC-CTSAT</executorOrgCode><smsCode>14</smsCode></application>";
                String confirmOrderSigned = extractOrderSimple(xmlString, applicationId, base64photo, applicantNumber, true);
                String denyOrderSigned = extractOrderSimple(xmlString, applicationId, null, applicantNumber, false);
                jsonNode.put("applicationId", applicationId);
                jsonNode.put("confirmOrderSign", confirmOrderSigned);
                jsonNode.put("denyOrderSign", denyOrderSigned);
                jsonNode.put("digiSign", digiSign);
            }

            return objectMapper.writerWithDefaultPrettyPrinter()
                    .writeValueAsString(jsonNode);

        } catch (Exception e) {
            log.error("Error converting XML to JSON", e);
            return "{\"error\": \"Failed to parse XML\"}";
        }
    }

    public static String extractOrderSimple(String xml, String applicationId, String base64photo, String applicantNumber, Boolean isConfirm) {
        if (xml == null) return null;

        xml = xml.replaceAll("&#13;", "").trim();

        int start = xml.indexOf("<ns4:order");
        int end = xml.indexOf("</ns4:order>");

        if (start == -1 || end == -1) {
            throw new RuntimeException("order tag not found");
        }

        String orderPart = xml.substring(start, end + "</ns4:order>".length());

        orderPart = orderPart.replaceAll("(<|</)\\w*:", "$1");

        orderPart = orderPart.replaceFirst("<order", "<order xmlns=\"http://pki.gov.kz/api/ws/iiscon/wsdl\"");
        if (orderPart.contains("<digitalDoc>") && isConfirm) {
            String replacement = "</digitalDoc>" +
                    "<applicationId>"+ applicationId + "</applicationId>" +
                    "<applicantNumber>"+ applicantNumber + "</applicantNumber>" +
                    "<savedPhoto>" + base64photo + "</savedPhoto>" +
                    "<digitalDoc>false</digitalDoc>";

            if (!orderPart.contains("<applicationType>")) {
                replacement += "<applicationType>scan</applicationType>";
            }

            orderPart = orderPart.replaceFirst("</digitalDoc>", java.util.regex.Matcher.quoteReplacement(replacement));
        } else if (orderPart.contains("<digitalDoc>") && !isConfirm){
            String replacement = "</digitalDoc>" +
                    "<applicationId>"+ applicationId + "</applicationId>" +
                    "<applicantNumber>"+ applicantNumber + "</applicantNumber>" +
                    "<digitalDoc>false</digitalDoc>";

            if (!orderPart.contains("<applicationType>")) {
                replacement += "<applicationType>scan</applicationType>";
            }

            orderPart = orderPart.replaceFirst("</digitalDoc>", java.util.regex.Matcher.quoteReplacement(replacement));
        }

        String orderXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" + orderPart;

        return orderXml
                .replace("\\\"", "\"")  // убираем экранированные кавычки
                .replace("\\n", "")     // убираем \n
                .replace("\\r", "");
    }


    /**
     * Рекурсивно парсит XML элемент в JSON
     */
    private static void parseElement(Element element, ObjectNode parentNode) {
        String nodeName = getCleanName(element);

        NodeList children = element.getChildNodes();
        boolean hasElementChildren = false;
        StringBuilder textContent = new StringBuilder();

        // Проверяем, есть ли дочерние элементы
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                hasElementChildren = true;
            } else if (child.getNodeType() == Node.TEXT_NODE) {
                String text = child.getTextContent().trim();
                if (!text.isEmpty()) {
                    textContent.append(text);
                }
            }
        }

        if (hasElementChildren) {
            // Элемент имеет дочерние элементы
            ObjectNode childNode = objectMapper.createObjectNode();

            for (int i = 0; i < children.getLength(); i++) {
                Node child = children.item(i);
                if (child.getNodeType() == Node.ELEMENT_NODE) {
                    parseElement((Element) child, childNode);
                }
            }

            parentNode.set(nodeName, childNode);
        } else {
            // Листовой элемент - добавляем текст
            String text = textContent.toString();
            if (!text.isEmpty()) {
                parentNode.put(nodeName, text);
            } else {
                parentNode.put(nodeName, "");
            }
        }
    }

    /**
     * Убирает namespace префикс из имени элемента
     */
    private static String getCleanName(Element element) {
        String localName = element.getLocalName();
        return localName != null ? localName : element.getNodeName();
    }

    /**
     * Конвертирует XML в JSON без pretty print
     */
    public static String xmlToJsonCompact(String xmlString) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();

            ByteArrayInputStream input = new ByteArrayInputStream(
                    xmlString.getBytes(StandardCharsets.UTF_8)
            );
            Document doc = builder.parse(input);

            ObjectNode jsonNode = objectMapper.createObjectNode();
            Element root = doc.getDocumentElement();

            parseElement(root, jsonNode);

            return objectMapper.writeValueAsString(jsonNode);

        } catch (Exception e) {
            log.error("Error converting XML to JSON", e);
            return "{\"error\": \"Failed to parse XML\"}";
        }
    }

    public static String cleanXml(String xml){
        if (xml == null) return null;

        xml = xml.replaceAll("&#13;", "").trim();
        xml = xml.replaceAll(" +", " ");

        return xml
                .replace("\\\"", "\"");

    }

    /**
     * Извлекает order из XML, удаляет префиксы и лишние символы
     * Возвращает чистый XML с декларацией и namespace
     */
    public static String extractAndCleanOrderXml(Object response) {
        try {
            // Конвертируем response в JSON
            String responseJson = objectMapper.writeValueAsString(response);
            JsonNode rootNode = objectMapper.readTree(responseJson);

            // Получаем XML строку из responseData.data
            JsonNode dataNode = rootNode.at("/responseData/data");

            if (dataNode.isMissingNode() || dataNode.isNull()) {
                log.warn("Data node not found in response");
                return null;
            }

            String xmlString = dataNode.asText();

            // Парсим XML
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            DocumentBuilder builder = factory.newDocumentBuilder();

            ByteArrayInputStream input = new ByteArrayInputStream(
                    xmlString.getBytes(StandardCharsets.UTF_8)
            );
            Document doc = builder.parse(input);

            // Создаем новый документ без префиксов
            DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder newBuilder = newFactory.newDocumentBuilder();
            Document newDoc = newBuilder.newDocument();

            // Создаем корневой элемент с namespace
            Element newRoot = newDoc.createElementNS("http://pki.gov.kz/api/ws/iiscon/wsdl", "order");
            newDoc.appendChild(newRoot);

            // Копируем содержимое, удаляя префиксы
            Element originalRoot = doc.getDocumentElement();
            copyElementWithoutPrefixes(originalRoot, newRoot, newDoc);

            // Конвертируем в строку
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
            transformer.setOutputProperty(OutputKeys.INDENT, "no");
//            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(newDoc), new StreamResult(writer));

            String result = writer.toString();

            int orderEndIndex = result.indexOf("</order>");
            if (orderEndIndex != -1) {
                result = result.substring(0, orderEndIndex + "</order>".length());
            }

            return result;

        } catch (Exception e) {
            log.error("Error extracting and cleaning order XML", e);
            return null;
        }
    }

    /**
     * Рекурсивно копирует элементы без namespace префиксов
     */
    private static void copyElementWithoutPrefixes(Element source, Element target, Document targetDoc) {
        NodeList children = source.getChildNodes();

        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);

            if (child.getNodeType() == Node.ELEMENT_NODE) {
                Element childElement = (Element) child;
                String localName = childElement.getLocalName();
                if (localName == null) {
                    localName = childElement.getNodeName();
                    // Удаляем префикс из имени, если он есть
                    if (localName.contains(":")) {
                        localName = localName.substring(localName.indexOf(":") + 1);
                    }
                }

                Element newChild = targetDoc.createElement(localName);
                target.appendChild(newChild);

                // Рекурсивно копируем дочерние элементы
                copyElementWithoutPrefixes(childElement, newChild, targetDoc);

            } else if (child.getNodeType() == Node.TEXT_NODE) {
                String text = child.getTextContent();
                if (text != null && !text.trim().isEmpty()) {
                    // Нормализуем пробелы и удаляем переносы строк
                    String normalizedText = text.replaceAll("[\r\n]+", " ").trim();
                    if (!normalizedText.isEmpty()) {
                        target.appendChild(targetDoc.createTextNode(normalizedText));
                    }
                }
            }
        }
    }
}