{{>licenseInfo}}

package {{package}};

{{#gson}}
import com.google.gson.reflect.TypeToken;
{{/gson}}
{{#imports}}import {{import}};
{{/imports}}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
{{#jackson}}
import io.restassured.common.mapper.TypeRef;
{{/jackson}}
import io.restassured.http.Method;
import io.restassured.response.Response;
{{#swagger1AnnotationLibrary}}
import io.swagger.annotations.*;
{{/swagger1AnnotationLibrary}}
{{#swagger2AnnotationLibrary}}
import io.swagger.v3.oas.annotations.*;
import io.swagger.v3.oas.annotations.enums.*;
import io.swagger.v3.oas.annotations.media.*;
import io.swagger.v3.oas.annotations.responses.*;
import io.swagger.v3.oas.annotations.security.*;
{{/swagger2AnnotationLibrary}}

{{#useBeanValidation}}
import jakarta.validation.constraints.*;
import jakarta.validation.Valid;
{{/useBeanValidation}}

import java.lang.reflect.Type;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
{{#gson}}
import {{invokerPackage}}.JSON;
{{/gson}}
import static io.restassured.http.Method.*;

{{#swagger1AnnotationLibrary}}
@Api(value = "{{{baseName}}}")
{{/swagger1AnnotationLibrary}}
{{#swagger2AnnotationLibrary}}
@Tag(name = "{{{baseName}}}")
{{/swagger2AnnotationLibrary}}
public class {{classname}} {

    private Supplier<RequestSpecBuilder> reqSpecSupplier;
    private Consumer<RequestSpecBuilder> reqSpecCustomizer;

    private {{classname}}(Supplier<RequestSpecBuilder> reqSpecSupplier) {
        this.reqSpecSupplier = reqSpecSupplier;
    }

    public static {{classname}} {{classVarName}}(Supplier<RequestSpecBuilder> reqSpecSupplier) {
        return new {{classname}}(reqSpecSupplier);
    }

    private RequestSpecBuilder createReqSpec() {
        RequestSpecBuilder reqSpec = reqSpecSupplier.get();
        if(reqSpecCustomizer != null) {
            reqSpecCustomizer.accept(reqSpec);
        }
        return reqSpec;
    }

    public List<Oper> getAllOperations() {
        return Arrays.asList(
{{#operations}}
{{#operation}}
                {{operationId}}(){{^-last}},{{/-last}}
{{/operation}}
{{/operations}}
        );
    }
{{#operations}}
{{#operation}}

{{#swagger1AnnotationLibrary}}
    @ApiOperation(value = "{{{summary}}}",
            notes = "{{{notes}}}",
            nickname = "{{{operationId}}}",
            tags = { {{#tags}}{{#name}}"{{{.}}}"{{/name}}{{^-last}}, {{/-last}}{{/tags}} })
    @ApiResponses(value = { {{#responses}}
            @ApiResponse(code = {{{code}}}, message = "{{{message}}}") {{^-last}},{{/-last}}{{/responses}} })
{{/swagger1AnnotationLibrary}}
{{#swagger2AnnotationLibrary}}
    @Operation(summary = "{{{summary}}}",
            description = "{{{notes}}}",
            operationId = "{{{operationId}}}",
            tags = { {{#tags}}{{#name}}"{{{.}}}"{{/name}}{{^-last}}, {{/-last}}{{/tags}} })
    @ApiResponses(value = { {{#responses}}
            @ApiResponse(responseCode = "{{{code}}}", description = "{{{message}}}") {{^-last}},{{/-last}}{{/responses}} })
{{/swagger2AnnotationLibrary}}
    {{#isDeprecated}}
    @Deprecated
    {{/isDeprecated}}
    public {{operationIdCamelCase}}Oper {{operationId}}() {
        return new {{operationIdCamelCase}}Oper(createReqSpec());
    }
{{/operation}}
{{/operations}}

    /**
     * Customize request specification
     * @param reqSpecCustomizer consumer to modify the RequestSpecBuilder
     * @return api
     */
    public {{classname}} reqSpec(Consumer<RequestSpecBuilder> reqSpecCustomizer) {
        this.reqSpecCustomizer = reqSpecCustomizer;
        return this;
    }

    {{#operations}}
    {{#operation}}
    /**
     * {{summary}}
     * {{notes}}
     *
     {{#allParams}}
     * @see #{{#isPathParam}}{{paramName}}Path{{/isPathParam}}{{#isQueryParam}}{{paramName}}Query{{/isQueryParam}}{{#isFormParam}}{{^isFile}}{{paramName}}Form{{/isFile}}{{#isFile}}{{paramName}}MultiPart{{/isFile}}{{/isFormParam}}{{#isHeaderParam}}{{paramName}}Header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
     {{/allParams}}
     {{#returnType}}
     * return {{.}}
     {{/returnType}}
     {{#isDeprecated}}
     * @deprecated
     {{/isDeprecated}}
     {{#externalDocs}}
     * {{description}}
     * @see <a href="{{url}}">{{summary}} Documentation</a>
    {{/externalDocs}}
     */
    {{#isDeprecated}}
    @Deprecated
    {{/isDeprecated}}
    public static class {{operationIdCamelCase}}Oper implements Oper {

        public static final Method REQ_METHOD = {{httpMethod}};
        public static final String REQ_URI = "{{path}}";

        private RequestSpecBuilder reqSpec;
        private ResponseSpecBuilder respSpec;

        public {{operationIdCamelCase}}Oper(RequestSpecBuilder reqSpec) {
            this.reqSpec = reqSpec;
            {{#vendorExtensions}}
            {{#x-content-type}}
            reqSpec.setContentType("{{x-content-type}}");
            {{/x-content-type}}
            reqSpec.setAccept("{{#x-accepts}}{{.}}{{^-last}},{{/-last}}{{/x-accepts}}");
            {{/vendorExtensions}}
            this.respSpec = new ResponseSpecBuilder();
        }

        /**
         * {{httpMethod}} {{path}}
         * @param handler handler
         * @param <T> type
         * @return type
         */
        @Override
        public <T> T execute(Function<Response, T> handler) {
            return handler.apply(RestAssured.given().spec(reqSpec.build()).expect().spec(respSpec.build()).when().request(REQ_METHOD, REQ_URI));
        }
        {{#returnType}}

        /**
         * {{httpMethod}} {{path}}
         * @param handler handler
         * @return {{returnType}}
         */
        public {{{returnType}}} executeAs(Function<Response, Response> handler) {
            {{#gson}}Type type = new TypeToken<{{{returnType}}}>(){}.getType();
            {{/gson}}{{#jackson}}TypeRef<{{{returnType}}}> type = new TypeRef<{{{returnType}}}>(){};
            {{/jackson}}return execute(handler).as(type);
        }
        {{/returnType}}
        {{#bodyParams}}

         /**
         * @param {{paramName}} ({{dataType}}) {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
         * @return operation
         */
        public {{operationIdCamelCase}}Oper body({{{dataType}}} {{paramName}}) {
            reqSpec.setBody({{paramName}});
            return this;
        }
        {{/bodyParams}}
        {{#headerParams}}

        public static final String {{#convert}}{{paramName}}{{/convert}}_HEADER = "{{baseName}}";

        /**
         * @param {{paramName}} ({{dataType}}) {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
         * @return operation
         */
        public {{operationIdCamelCase}}Oper {{paramName}}Header(String {{paramName}}) {
            reqSpec.addHeader({{#convert}}{{paramName}}{{/convert}}_HEADER, {{paramName}});
            return this;
        }
        {{/headerParams}}
        {{#pathParams}}

        public static final String {{#convert}}{{paramName}}{{/convert}}_PATH = "{{baseName}}";

        /**
         * @param {{paramName}} ({{dataType}}) {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
         * @return operation
         */
        public {{operationIdCamelCase}}Oper {{paramName}}Path(Object {{paramName}}) {
            reqSpec.addPathParam({{#convert}}{{paramName}}{{/convert}}_PATH, {{paramName}});
            return this;
        }
        {{/pathParams}}
        {{#queryParams}}

        public static final String {{#convert}}{{paramName}}{{/convert}}_QUERY = "{{baseName}}";

        /**
         * @param {{paramName}} ({{dataType}}) {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
         * @return operation
         */
        public {{operationIdCamelCase}}Oper {{paramName}}Query(Object... {{paramName}}) {
            reqSpec.addQueryParam({{#convert}}{{paramName}}{{/convert}}_QUERY, {{paramName}});
            return this;
        }
        {{/queryParams}}
        {{#formParams}}
        {{^isFile}}

         public static final String {{#convert}}{{paramName}}{{/convert}}_FORM = "{{baseName}}";

         /**
         * @param {{paramName}} ({{dataType}}) {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
         * @return operation
         */
         public {{operationIdCamelCase}}Oper {{paramName}}Form(Object... {{paramName}}) {
            reqSpec.addFormParam({{#convert}}{{paramName}}{{/convert}}_FORM, {{paramName}});
            return this;
         }
        {{/isFile}}
        {{/formParams}}
        {{#formParams}}
        {{#isFile}}

         /**
         * It will assume that the control name is file and the &lt;content-type&gt; is &lt;application/octet-stream&gt;
         * @see #reqSpec for customise
         * @param {{paramName}} ({{dataType}}) {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}}
         * @return operation
         */
         public {{operationIdCamelCase}}Oper {{paramName}}MultiPart({{{dataType}}} {{paramName}}) {
            reqSpec.addMultiPart({{paramName}});
            return this;
         }
        {{/isFile}}
        {{/formParams}}

        /**
         * Customize request specification
         * @param reqSpecCustomizer consumer to modify the RequestSpecBuilder
         * @return operation
         */
        public {{operationIdCamelCase}}Oper reqSpec(Consumer<RequestSpecBuilder> reqSpecCustomizer) {
            reqSpecCustomizer.accept(reqSpec);
            return this;
        }

        /**
         * Customize response specification
         * @param respSpecCustomizer consumer to modify the ResponseSpecBuilder
         * @return operation
         */
        public {{operationIdCamelCase}}Oper respSpec(Consumer<ResponseSpecBuilder> respSpecCustomizer) {
            respSpecCustomizer.accept(respSpec);
            return this;
        }
    }
{{/operation}}
{{/operations}}
}
