/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.quarkus.component.cxf.soap.client.it;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import com.sun.xml.messaging.saaj.soap.ver1_1.Message1_1Impl;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.xml.soap.MessageFactory;
import jakarta.xml.soap.SOAPMessage;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.cxf.common.DataFormat;
import org.apache.camel.component.cxf.common.message.CxfConstants;
import org.apache.camel.component.cxf.jaxws.CxfEndpoint;
import org.apache.cxf.ext.logging.LoggingFeature;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.eap.quickstarts.wscalculator.basicauthcalculator.BasicAuthCalculatorService;
import org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService;

@ApplicationScoped
public class CxfSoapClientRoutes extends RouteBuilder {

    @Inject
    @Named("loggingFeatureClient")
    LoggingFeature loggingFeature;

    @ConfigProperty(name = "camel-quarkus.it.calculator.baseUri")
    String serviceBaseUri;

    public static final String MESSAGE_RAW_SIMPLE_ADD = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ser=\"http://www.jboss.org/eap/quickstarts/wscalculator/Calculator\">\n"
            +
            "   <soapenv:Header/>\n" +
            "   <soapenv:Body>\n" +
            "      <ser:add>\n" +
            "         <arg0>%s</arg0>\n" +
            "         <arg1>%s</arg1>\n" +
            "      </ser:add>\n" +
            "   </soapenv:Body>\n" +
            "</soapenv:Envelope>";

    @Override
    public void configure() {

        from("direct:simpleUriBean")
                .to("cxf:bean:soapClientEndpointPojo");

        from("direct:simpleUriAddress")
                .to(String.format("cxf://%s?wsdlURL=%s&dataFormat=POJO&serviceClass=%s", calculatorServiceAddress(),
                        calculatorServiceWsdlUrl(), CalculatorService.class.getName()));

        from("direct:simpleAddDataFormat")
                .process(exchange -> {
                    Map<String, Object> headers = exchange.getIn().getHeaders();
                    String endpointDataFormat = headers.get("endpointDataFormat").toString();
                    int[] numbers = exchange.getIn().getBody(int[].class);
                    String xmlRequest = String.format(MESSAGE_RAW_SIMPLE_ADD, numbers[0], numbers[1]);
                    if (DataFormat.RAW.name().equals(endpointDataFormat)) {
                        exchange.getIn().setBody(xmlRequest);
                    } else if (DataFormat.CXF_MESSAGE.name().equals(endpointDataFormat)) {
                        try (InputStream is = new ByteArrayInputStream(xmlRequest.getBytes(StandardCharsets.UTF_8))) {
                            SOAPMessage requestMsg = MessageFactory.newInstance().createMessage(null, is);
                            exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, "add");
                            exchange.getIn().setBody(new Message1_1Impl(requestMsg));
                        }
                    }
                })
                .toD("cxf:bean:soapClientEndpoint${header.endpointDataFormat}");

        from("direct:operandsAdd")
                .setHeader(CxfConstants.OPERATION_NAME).constant("addOperands")
                .to("cxf:bean:soapClientEndpointPojo");

        from("direct:basicAuthAdd")
                .to("cxf:bean:basicAuthAddClientEndpoint?username={{cq.cxf.it.calculator.auth.basic.user}}&password={{cq.cxf.it.calculator.auth.basic.password}}");

        from("direct:basicAuthAddAnonymous")
                .to("cxf:bean:basicAuthAddAnonymousClientEndpoint");

    }

    @Produces
    @ApplicationScoped
    @Named("loggingFeatureClient")
    public LoggingFeature loggingFeature() {
        final LoggingFeature result = new LoggingFeature();
        result.setPrettyLogging(true);
        return result;
    }

    @Produces
    @ApplicationScoped
    @Named
    CxfEndpoint soapClientEndpointPojo() {
        return soapClientCxfEndpoint(DataFormat.POJO);
    }

    @Produces
    @ApplicationScoped
    @Named("soapClientEndpointRAW")
    CxfEndpoint soapClientEndpointRaw() {
        return soapClientCxfEndpoint(DataFormat.RAW);
    }

    @Produces
    @ApplicationScoped
    @Named("soapClientEndpointCXF_MESSAGE")
    CxfEndpoint soapClientEndpointCxfMessage() {
        return soapClientCxfEndpoint(DataFormat.CXF_MESSAGE);
    }

    private CxfEndpoint soapClientCxfEndpoint(DataFormat dataFormat) {
        final CxfEndpoint result = new CxfEndpoint();
        result.setDataFormat(dataFormat);
        result.setServiceClass(CalculatorService.class);
        result.setAddress(calculatorServiceAddress());
        result.setWsdlURL(calculatorServiceWsdlUrl());
        result.getFeatures().add(loggingFeature);
        return result;
    }

    @Produces
    @ApplicationScoped
    @Named
    CxfEndpoint basicAuthAddClientEndpoint() {
        return basicAuthCxfEndpoint();
    }

    @Produces
    @ApplicationScoped
    @Named
    CxfEndpoint basicAuthAddAnonymousClientEndpoint() {
        return basicAuthCxfEndpoint();
    }

    private CxfEndpoint basicAuthCxfEndpoint() {
        final CxfEndpoint result = new CxfEndpoint();
        result.setDataFormat(DataFormat.POJO);
        result.setServiceClass(BasicAuthCalculatorService.class);
        result.setAddress(serviceBaseUri + "/calculator-ws/BasicAuthCalculatorService");
        result.setWsdlURL("wsdl/BasicAuthCalculatorService.wsdl");
        result.getFeatures().add(loggingFeature);
        return result;
    }

    private String calculatorServiceAddress() {
        return serviceBaseUri + "/calculator-ws/CalculatorService";
    }

    private String calculatorServiceWsdlUrl() {
        return "wsdl/CalculatorService.wsdl";
    }

}
