/*
 * 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.openapijava.it;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Set;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.core.MediaType;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.CollectionFormat;
import org.apache.camel.model.rest.RestBindingMode;
import org.apache.camel.model.rest.RestParamType;
import org.apache.camel.quarkus.component.openapijava.it.model.AllOfFormWrapper;
import org.apache.camel.quarkus.component.openapijava.it.model.AnyOfFormWrapper;
import org.apache.camel.quarkus.component.openapijava.it.model.Fruit;
import org.apache.camel.quarkus.component.openapijava.it.model.OneOfFormWrapper;

@ApplicationScoped
public class OpenApiRoutes extends RouteBuilder {

    @Override
    public void configure() throws Exception {

        restConfiguration()
                .apiVendorExtension(true)
                .useXForwardHeaders(false)
                .host("localhost")
                .port(8080)
                .apiProperty("api.title", "Camel Quarkus API")
                .apiProperty("api.description", "The Awesome Camel Quarkus REST API")
                .apiProperty("api.version", "1.2.3")
                .apiProperty("openapi.version", "3.0.0")
                .apiProperty("cors", "true")
                .apiProperty("schemes", "http,https")
                .apiProperty("api.path", "/api-docs")
                .apiProperty("base.path", "/")
                .apiProperty("api.termsOfService", "https://camel.apache.org")
                .apiProperty("api.contact.name", "Mr Camel Quarkus")
                .apiProperty("api.contact.email", "mrcq@cq.org")
                .apiProperty("api.contact.url", "https://camel.apache.org")
                .apiProperty("api.license.name", "Apache V2")
                .apiProperty("api.license.url", "https://www.apache.org/licenses/LICENSE-2.0");

        rest("/api")
                .get("/fruits/list")
                .type(Fruit.class)
                .description("Gets a list of fruits")
                .id("list")
                .produces(MediaType.APPLICATION_JSON)
                .to("direct:fruits")

                .get("/operation/spec")
                .param()
                .type(RestParamType.header)
                .name("header_number")
                .dataType("integer")
                .required(true)
                .allowableValues("1", "2", "3")
                .defaultValue("1")
                .description("Header Param Number")
                .endParam()
                .param()
                .type(RestParamType.query)
                .name("query_letter")
                .dataType("string")
                .required(false)
                .allowableValues("A", "B", "C")
                .defaultValue("B")
                .description("Query Param Letter")
                .collectionFormat(CollectionFormat.multi)
                .endParam()
                .responseMessage()
                .code(418)
                .message("I am a teapot")
                .responseModel(Integer.class)
                .header("rate")
                .description("API Rate Limit")
                .dataType("integer")
                .endHeader()
                .endResponseMessage()
                .responseMessage()
                .code("error")
                .message("Response Error")
                .endResponseMessage()
                .to("seda:echoMethodPath")

                .get("/security/scopes")
                .security("OAuth2", "scope1,scope2,scope3")
                .to("seda:echoMethodPath")

                .get("/security/api/key")
                .to("seda:echoMethodPath")
                .securityDefinitions()
                .apiKey("X-API-Key", "The API key")
                .withHeader("X-API-KEY")
                .end()
                .end()

                .get("/security/basic/auth")
                .to("seda:echoMethodPath")
                .securityDefinitions()
                .basicAuth("basicAuth", "Basic Authentication")
                .end()

                .get("/security/oauth2")
                .to("seda:echoMethodPath")
                .securityDefinitions()
                .oauth2("oauth2", "OAuth2 Authentication")
                .flow("implicit")
                .authorizationUrl("https://secure.apache.org/fake/oauth2/authorize")
                .withScope("scope1", "Scope 1")
                .withScope("scope2", "Scope 2")
                .withScope("scope3", "Scope 3")
                .end()
                .end();

        rest()
                .get("/security/bearer/token")
                .to("seda:echoMethodPath")
                .securityDefinitions()
                .bearerToken("bearerAuth", "Bearer Token Authentication")
                .end()

                .get("/security/mutual/tls")
                .to("seda:echoMethodPath")
                .securityDefinitions()
                .mutualTLS("mutualTLS")
                .end()

                .get("/security/openid")
                .to("seda:echoMethodPath")
                .securityDefinitions()
                .openIdConnect("openId", "https://secure.apache.org/fake/openid-configuration")
                .end()

                .get("/api/operation/spec/array/params")
                .param()
                .name("string_array")
                .dataType("array")
                .arrayType("string")
                .allowableValues("A", "B", "C")
                .endParam()
                .param()
                .name("int_array")
                .dataType("array")
                .arrayType("int")
                .allowableValues("1", "2", "3")
                .endParam()
                .param()
                .name("integer_array")
                .dataType("array")
                .arrayType("integer")
                .allowableValues("1", "2", "3")
                .endParam()
                .param()
                .name("long_array")
                .dataType("array")
                .arrayType("long")
                .allowableValues("1", "2", "3")
                .endParam()
                .param()
                .name("float_array")
                .dataType("array")
                .arrayType("float")
                .allowableValues("1.0", "2.0", "3.0")
                .endParam()
                .param()
                .name("double_array")
                .dataType("array")
                .arrayType("double")
                .allowableValues("1.0", "2.0", "3.0")
                .endParam()
                .param()
                .name("boolean_array")
                .dataType("array")
                .arrayType("boolean")
                .allowableValues("true", "false")
                .endParam()
                .param()
                .name("byte_array")
                .dataType("array")
                .arrayType("byte")
                .allowableValues("1", "2", "3")
                .endParam()
                .param()
                .name("binary_array")
                .dataType("array")
                .arrayType("binary")
                .allowableValues("1", "2", "3")
                .endParam()
                .param()
                .name("date_array")
                .dataType("array")
                .arrayType("date")
                .allowableValues("2023-01-01", "2023-02-02", "2023-03-03")
                .endParam()
                .param()
                .name("datetime_array")
                .dataType("array")
                .arrayType("date-time")
                .allowableValues("2023-01-01T11:11:11+01:00", "2023-02-02T12:12:12+01:00", "2023-03-03T13:13:13+01:00")
                .endParam()
                .param()
                .name("password_array")
                .dataType("array")
                .arrayType("password")
                .allowableValues("foo", "bar", "cheese")
                .endParam()
                .to("seda:echoMethodPath");

        rest("/form")
                .post("/oneOf")
                .tag("OneOf")
                .bindingMode(RestBindingMode.json)
                .description("OneOf rest service")

                .consumes("application/json")
                .produces("application/json")
                .type(OneOfFormWrapper.class)
                .responseMessage()
                .code(200).message("Ok")
                .endResponseMessage()

                .to("seda:res");

        rest("/form")
                .post("/allOf")
                .tag("AllOf")
                .bindingMode(RestBindingMode.json)
                .description("AllOf rest service")

                .consumes("application/json")
                .produces("application/json")
                .type(AllOfFormWrapper.class)
                .responseMessage()
                .code(200).message("Ok")
                .endResponseMessage()

                .to("seda:res");

        rest("/form")
                .post("/anyOf")
                .tag("AnyOf")
                .bindingMode(RestBindingMode.json)
                .description("AnyOf rest service")

                .consumes("application/json")
                .produces("application/json")
                .type(AnyOfFormWrapper.class)
                .responseMessage()
                .code(200).message("Ok")
                .endResponseMessage()

                .to("seda:res");

        from("direct:fruits")
                .setBody().constant(getFruits())
                .marshal().json();

        from("seda:echoMethodPath")
                .setBody().simple("${header.CamelHttpMethod}: ${header.CamelHttpPath}");

        from("seda:res")
                .setBody(constant("{\"result\": \"Ok\"}"));
    }

    private Set<Fruit> getFruits() {
        Set<Fruit> fruits = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));
        fruits.add(new Fruit("Apple", "Winter fruit", 10));
        fruits.add(new Fruit("Pineapple", "Tropical fruit", 20));
        return fruits;
    }
}
