/*
 * 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.http.vertx;

import java.nio.charset.StandardCharsets;

import io.vertx.core.buffer.Buffer;
import io.vertx.ext.web.multipart.MultipartForm;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.PropertyBindingException;
import org.apache.camel.ResolveEndpointFailedException;
import org.apache.camel.http.base.HttpOperationFailedException;
import org.apache.camel.quarkus.component.http.common.AbstractHttpResource;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;

import static org.apache.camel.component.vertx.http.VertxHttpConstants.CONTENT_TYPE_FORM_URLENCODED;

@Path("/test/client/vertx-http")
@ApplicationScoped
public class VertxResource extends AbstractHttpResource {

    @Override
    @Path("/get")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String httpGet(@QueryParam("test-port") int port) {
        return producerTemplate
                .toF("vertx-http:http://localhost:%d/service/common/get", port)
                .withHeader(Exchange.HTTP_METHOD, "GET")
                .request(String.class);
    }

    @Override
    @Path("/post")
    @POST
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.TEXT_PLAIN)
    public String httpPost(@QueryParam("test-port") int port, String message) {
        return producerTemplate
                .toF("vertx-http:http://localhost:%d/service/common/toUpper", port)
                .withBody(message)
                .withHeader(Exchange.CONTENT_TYPE, MediaType.TEXT_PLAIN)
                .withHeader(Exchange.HTTP_METHOD, "POST")
                .request(String.class);
    }

    @Override
    @Path("/auth/basic")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response basicAuth(@QueryParam("test-port") int port, @QueryParam("username") String username,
            @QueryParam("password") String password) {
        Exchange result = producerTemplate
                .withHeader(Exchange.HTTP_QUERY, "component=vertx-http")
                .toF("vertx-http:http://localhost:%d/test/client/vertx-http/auth/basic/secured?throwExceptionOnFailure=false"
                        + "&basicAuthUsername=%s"
                        + "&basicAuthPassword=%s", port, username, password)
                .send();

        Integer status = result.getMessage().getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
        String body = result.getMessage().getBody(String.class);
        return Response.status(status).entity(body).build();
    }

    @Override
    @Path("/proxy")
    @GET
    @Produces(MediaType.APPLICATION_XML)
    public String httpProxy() {
        Config config = ConfigProvider.getConfig();
        String proxyHost = config.getValue("proxy.host", String.class);
        Integer proxyPort = config.getValue("proxy.port", Integer.class);
        return producerTemplate
                .toF("vertx-http:%s?"
                        + "proxyHost=%s"
                        + "&proxyPort=%d"
                        + "&proxyType=HTTP"
                        + "&proxyUsername=%s"
                        + "&proxyPassword=%s", String.format(PROXIED_URL, "vertx-http"), proxyHost, proxyPort, USER_ADMIN,
                        USER_ADMIN_PASSWORD)
                .request(String.class);
    }

    @Path("/compression")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String compression() {
        return producerTemplate
                .toF("vertx-http:http://localhost:%d/service/common/compress?useCompression=true",
                        ConfigProvider.getConfig().getValue("quarkus.http.test-port", Integer.class))
                .withHeader(Exchange.HTTP_METHOD, "GET")
                .withHeader("Accept-Encoding", "gzip, deflate")
                .request(String.class);
    }

    @Path("/serialized/exception")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String vertxHttpSerializedException(@QueryParam("test-port") int port) {
        Exchange exchange = producerTemplate
                .toF("vertx-http:http://localhost:%d/test/server/serialized/exception?transferException=true", port)
                .withHeader(Exchange.HTTP_METHOD, "GET")
                .send();
        return exchange.getException().getClass().getName();
    }

    @Path("/multipart-form-params")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String vertxHttpMultipartFormParams(@QueryParam("test-port") int port,
            @QueryParam("organization") String organization,
            @QueryParam("project") String project) {
        return producerTemplate
                .toF("vertx-http:http://localhost:%d/service/multipart-form-params", port)
                .withBody("organization=" + organization + "&project=" + project)
                .withHeader(Exchange.CONTENT_TYPE, CONTENT_TYPE_FORM_URLENCODED)
                .request(String.class);
    }

    @Path("/multipart-form-data")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String vertxHttpMultipartFormData(@QueryParam("test-port") int port) {
        MultipartForm form = MultipartForm.create();
        form.binaryFileUpload("part-1", "test1.txt", Buffer.buffer("part1=content1".getBytes(StandardCharsets.UTF_8)),
                "text/plain");
        form.binaryFileUpload("part-2", "test2.xml",
                Buffer.buffer("<part2 value=\"content2\"/>".getBytes(StandardCharsets.UTF_8)), "text/xml");

        return producerTemplate
                .toF("vertx-http:http://localhost:%d/service/multipart-form-data", port)
                .withBody(form)
                .request(String.class);
    }

    @Path("/custom-vertx-options")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String vertxHttpCustomVertxOptions(@QueryParam("test-port") int port) {
        try {
            producerTemplate
                    .toF("vertx-http:http://localhost:%d/service/custom-vertx-options?vertxOptions=#myVertxOptions", port)
                    .request();
            return "NOT_EXPECTED: the custom vertxOptions should have triggered a ResolveEndpointFailedException";
        } catch (ResolveEndpointFailedException refex) {
            Throwable firstLevelExceptionCause = refex.getCause();
            if (firstLevelExceptionCause instanceof PropertyBindingException) {
                if (firstLevelExceptionCause.getCause() instanceof IllegalArgumentException) {
                    return "OK: the custom vertxOptions has triggered the expected exception";
                }
                return "NOT_EXPECTED: the 2nd level exception cause should be of type IllegalArgumentException";
            } else {
                return "NOT_EXPECTED: the 1st level exception cause should be of type PropertyBindingException";
            }
        }
    }

    @Path("/session-management")
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String vertxHttpSessionManagement(@QueryParam("test-port") int port) {
        String vertxHttpBaseUri = "vertx-http:http://localhost:" + port + "/service/session-management";
        Exchange result = producerTemplate
                .toF("%s/secure?sessionManagement=true&cookieStore=#myVertxCookieStore", vertxHttpBaseUri)
                .request(Exchange.class);

        HttpOperationFailedException exception = result.getException(HttpOperationFailedException.class);
        if (exception.getStatusCode() != 403) {
            return "NOT_EXPECTED: The first request in the session is expected to return HTTP 403";
        }

        result = producerTemplate
                .toF("%s/login?sessionManagement=true&cookieStore=#myVertxCookieStore", vertxHttpBaseUri)
                .withHeader("username", "my-username")
                .withHeader("password", "my-password")
                .request(Exchange.class);

        Message msg = result.getMessage();
        if (msg == null) {
            return "NOT_EXPECTED: The second request in the session should return a message";
        } else {
            String setCookieHeader = msg.getHeader("Set-Cookie", String.class);
            if (setCookieHeader == null || !setCookieHeader.contains("sessionId=my-session-id-123")) {
                return "NOT_EXPECTED: The message should have a Set-Cookie String header containing \"sessionId=my-session-id-123\"";
            }
        }

        return producerTemplate
                .toF("%s/secure?sessionManagement=true&cookieStore=#myVertxCookieStore", vertxHttpBaseUri)
                .request(String.class);
    }

    @Path("/buffer-conversion-with-charset")
    @GET
    public byte[] vertxBufferConversionWithCharset(@QueryParam("string") String string, @QueryParam("charset") String charset) {
        Buffer buffer = producerTemplate
                .to("direct:vertx-http-buffer-conversion-with-charset")
                .withBody(string)
                .withHeader(Exchange.CONTENT_TYPE, "text/plain;charset=" + charset)
                .request(Buffer.class);

        return buffer.getBytes();
    }
}
