package cz.moneta.test.harness.connectors;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.ws.BindingProvider;
import jakarta.xml.ws.Dispatch;
import jakarta.xml.ws.Service;
import jakarta.xml.ws.handler.MessageContext;
import org.apache.cxf.jaxws.CXFService;

import javax.xml.namespace.QName;
import java.net.URL;

public class WsConnector {

    private final Service service;
    private final QName portName;
    private final String address;
    private final String serviceNamespace;

    public WsConnector(String address, URL wsdlUrl, QName serviceName) {
        this.address = address;
        Service service = CXFService.create(wsdlUrl, serviceName);
        if (service.getPorts().hasNext()) {
            this.portName = service.getPorts().next();
        } else {
            throw new IllegalStateException(String.format("Service definition %s has no ports defined", wsdlUrl.toString()));
        }
        this.serviceNamespace = serviceName.getNamespaceURI();
        this.service = service;
    }

    @SuppressWarnings("unchecked")
    public <RESP> RESP invoke(Object request, Class<RESP> responseClass) {
        try {
            QName operationName = new QName(serviceNamespace, request.getClass().getSimpleName());
            JAXBContext jaxbContext = JAXBContext.newInstance(responseClass.getPackage().getName());
            Dispatch<Object> dispatch = service.createDispatch(portName, jaxbContext, Service.Mode.PAYLOAD);
            dispatch.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address);
            dispatch.getRequestContext().put(MessageContext.WSDL_OPERATION, operationName);
            return (RESP) dispatch.invoke(request);
        } catch (JAXBException e) {
            throw new IllegalArgumentException("Failed to invoke request", e);
        }
    }
}
