package at.vvo.omds.client.gui.api;

import at.vvo.omds.types.omds3.r2025_05.common.ProductsRequest;
import at.vvo.omds.types.omds3.r2025_05.common.ProductsResponse;
import at.vvo.omds.types.omds3.r2025_05.on2antrag.common.CalculateRequest;
import at.vvo.omds.types.omds3.r2025_05.on2antrag.common.CalculateResponse;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.soap.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;

public class SOAPConnector {

    static Logger logger = LoggerFactory.getLogger(SOAPConnector.class);

    public CalculateResponse calculateRequestAuskunft(String url, JAXBElement<CalculateRequest> request, String token) throws Exception {

        logger.info("Url:" + url);
        logger.info("Request VUNr:" + request.getValue().getVUNr());

        SOAPConnectionFactory connectionFactory = SOAPConnectionFactory.newInstance();
        SOAPConnection soapConnection = connectionFactory.createConnection();

        SOAPMessage req = createCalcRequest(request, token);

        SOAPMessage response = soapConnection.call(req, url);

        JAXBContext jaxbContext = JAXBContext.newInstance(at.vvo.omds.types.omds3.r2025_05.on2antrag.common.ObjectFactory.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();;

        System.out.println("-----------------------------------------------------------------------------");
        printMessage(response);
        System.out.println("-----------------------------------------------------------------------------");
        printMessage(req);

        CalculateResponse erg =
                (CalculateResponse) unmarshaller.unmarshal(response.getSOAPBody().extractContentAsDocument());
        soapConnection.close();

        return erg;
    }

    public ProductsResponse aprioriAuskunft(String url, JAXBElement<ProductsRequest> request, String token) throws Exception {

        logger.info("Url:" + url);
        logger.info("Request VUNr:" + request.getValue().getVUNr());

        SOAPConnectionFactory connectionFactory = SOAPConnectionFactory.newInstance();
        SOAPConnection soapConnection = connectionFactory.createConnection();

        SOAPMessage req = createRequest(request, token);

        SOAPMessage response = soapConnection.call(req, url);

        JAXBContext jaxbContext = JAXBContext.newInstance(at.vvo.omds.types.omds3.r2025_05.common.ObjectFactory.class, at.vvo.omds.types.omds2.v2_17.ObjectFactory.class, at.vvo.omds.types.omds3.r2025_05.on2antrag.common.ObjectFactory.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

        ProductsResponse erg =
                (ProductsResponse) unmarshaller.unmarshal(response.getSOAPBody().extractContentAsDocument());
        soapConnection.close();

       return erg;
    }

    private SOAPMessage createCalcRequest(JAXBElement<CalculateRequest> request, String token) throws Exception {
        MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
        SOAPMessage soapMessage = messageFactory.createMessage();

        createCalcEnvelope(soapMessage, request, token);

        MimeHeaders mimeHeaders = soapMessage.getMimeHeaders();
        mimeHeaders.addHeader("Content-Type", "application/soap+xml");

        soapMessage.saveChanges();

        return soapMessage;
    }

    private SOAPMessage createRequest(JAXBElement<ProductsRequest> request, String token) throws Exception {
        MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
        SOAPMessage soapMessage = messageFactory.createMessage();

        createEnvelope(soapMessage, request, token);

        MimeHeaders mimeHeaders = soapMessage.getMimeHeaders();
        mimeHeaders.addHeader("Content-Type", "application/soap+xml");

        soapMessage.saveChanges();

        return soapMessage;
    }

    private void createEnvelope(SOAPMessage soapMessage, JAXBElement<ProductsRequest> request, String token) throws Exception {
        SOAPPart soapPart = soapMessage.getSOAPPart();

        SOAPEnvelope envelope = soapPart.getEnvelope();
        SOAPHeader soapHeader = envelope.getHeader();

        Name headerElementName = envelope.createName(
                "Security",
                "wsse",
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        );
        SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(headerElementName);

        SOAPElement securityContextTokenSOAPElement = soapHeaderElement.addChildElement("SecurityContextToken", "wsc", "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512");
        securityContextTokenSOAPElement.addNamespaceDeclaration("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
        securityContextTokenSOAPElement.addAttribute(new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id", "wsu"), "authPolicy");
        SOAPElement identifierSOAPElement = securityContextTokenSOAPElement.addChildElement("Identifier", "wsc");
//        identifierSOAPElement.addTextNode(getToken(request.getValue().getVUNr()));
        identifierSOAPElement.addTextNode("eintollertoken");
        JAXBContext jaxbContext = JAXBContext.newInstance("at.vvo.omds.types.omds3.r2025_05.common");

        SOAPBody body = envelope.getBody();

        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        Marshaller marshaller = jaxbContext.createMarshaller();

        marshaller.marshal(request,document);

        body.addDocument(document);

        soapMessage.saveChanges();

    }

    private void createCalcEnvelope(SOAPMessage soapMessage, JAXBElement<CalculateRequest> request, String token) throws Exception {
        SOAPPart soapPart = soapMessage.getSOAPPart();

        SOAPEnvelope envelope = soapPart.getEnvelope();
        SOAPHeader soapHeader = envelope.getHeader();

        Name headerElementName = envelope.createName(
                "Security",
                "wsse",
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        );
        SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(headerElementName);

        SOAPElement securityContextTokenSOAPElement = soapHeaderElement.addChildElement("SecurityContextToken", "wsc", "http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512");
        securityContextTokenSOAPElement.addNamespaceDeclaration("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
        securityContextTokenSOAPElement.addAttribute(new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id", "wsu"), "authPolicy");
        SOAPElement identifierSOAPElement = securityContextTokenSOAPElement.addChildElement("Identifier", "wsc");
//        identifierSOAPElement.addTextNode(getToken(request.getValue().getVUNr()));
        identifierSOAPElement.addTextNode("eintollertoken");
        JAXBContext jaxbContext = JAXBContext.newInstance(at.vvo.omds.types.omds3.r2025_05.on2antrag.common.ObjectFactory.class);

        SOAPBody body = envelope.getBody();

        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        Marshaller marshaller = jaxbContext.createMarshaller();

        marshaller.marshal(request,document);

        body.addDocument(document);

        soapMessage.saveChanges();

    }


    public static void printMessage(SOAPMessage soapMessage) throws Exception {
        TransformerFactory tff = TransformerFactory.newInstance();
        Transformer tf = tff.newTransformer();

        tf.setOutputProperty(OutputKeys.INDENT, "yes");
        tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        Source sc = soapMessage.getSOAPPart().getContent();

        ByteArrayOutputStream streamOut = new ByteArrayOutputStream();
        StreamResult result = new StreamResult(streamOut);
        tf.transform(sc, result);

        String strMessage = streamOut.toString();

        logger.atInfo().log("----- SOAPMessage -----");
        logger.atInfo().log(strMessage);
    }

    public String getToken(String vunr) throws Exception {
        BufferedReader br = new BufferedReader(
                new FileReader(System.getProperty("user.home") + "\\OMDSSchlüssel.txt"));

        String[] tokens = br.lines().toArray(String[]::new);
        if (tokens.length > 1) {
            for (String token : tokens) {
                if (token.contains(vunr)) {
                    if (!(token.replaceAll(".*[=: ]\\s*", "").contains(vunr))){
                        return token.replaceAll(".*[=: ]\\s*","");
                    }
                    throw new Exception("Der Token mit der angegebenen VuNr hat ein ungültiges Format");
                }
            }
            throw new Exception("Kein Token für die angegebene VuNr vorhanden");
        }else{
            throw new Exception("Keine Tokens in der Datei");
        }
    }
}
