{{>licenseInfo}}
package {{apiPackage}}

{{#imports}}import {{import}}
{{/imports}}
import com.xebia.functional.openai.Config
import com.xebia.functional.openai.UploadFile
import com.xebia.functional.openai.appendGen
import com.xebia.functional.openai.generated.api.{{classname}}.*
import com.xebia.functional.openai.streamEvents
import com.xebia.functional.openai.errors.serializeOrThrowWithResponseInfo
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.timeout
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.accept
import io.ktor.client.request.header
import io.ktor.client.request.forms.formData
import io.ktor.client.request.parameter
import io.ktor.client.request.prepareRequest
import io.ktor.client.request.request
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.HttpStatement
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.contentType
import io.ktor.http.path
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit

{{#operations}}
/**
  *
  */
interface {{classname}} {
    {{#operation}}
        {{#allParams}}
            {{#isEnum}}

                /**
                * enum for parameter {{paramName}}
                */
                @Serializable
                {{#nonPublicApi}}internal {{/nonPublicApi}}enum class {{enumName}}{{operationIdCamelCase}}(val value: {{^isContainer}}{{dataType}}{{/isContainer}}{{#isContainer}}kotlin.String{{/isContainer}}) {
                {{^enumUnknownDefaultCase}}
                    {{#allowableValues}}{{#enumVars}}
                        @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}})
                        {{&name}}({{{value}}}){{^-last}},{{/-last}}
                    {{/enumVars}}{{/allowableValues}}
                {{/enumUnknownDefaultCase}}
                {{#enumUnknownDefaultCase}}
                    {{#allowableValues}}{{#enumVars}}{{^-last}}
                        @SerialName(value = {{^isString}}"{{/isString}}{{{value}}}{{^isString}}"{{/isString}})
                        {{&name}}({{{value}}}),
                    {{/-last}}{{/enumVars}}{{/allowableValues}}
                {{/enumUnknownDefaultCase}}
                }

            {{/isEnum}}
        {{/allParams}}

    /**
    * {{summary}}
    * {{notes}}
    {{#allParams}} * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}
    {{/allParams}} * @param configure optional configuration for the request, allows overriding the default configuration.
    * @return {{{returnType}}}{{^returnType}}void{{/returnType}}
    */
    suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{# isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}, {{/allParams}}configure: HttpRequestBuilder.() -> Unit = {}): {{#returnProperty}}{{#isFile}}HttpResponse{{/isFile}}{{^isFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isFile}}{{/returnProperty}}

    {{#vendorExtensions.x-streaming}}
    /**
    * Streaming variant: {{summary}}
    * By default, the client is modified to timeout after 60 seconds. Which is overridable by the [configure].
    * {{notes}}
    {{#allParams}} * @param {{{paramName}}} {{description}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{#required}}{{#defaultValue}} (default to {{{.}}}){{/defaultValue}}{{/required}}
    {{/allParams}} * @param configure optional configuration for the request, allows overriding the default configuration.
    * @return [Flow]<[{{{vendorExtensions.x-streaming-return}}}]>
    */
    fun {{operationId}}Stream({{#allParams}}{{{paramName}}}: {{#isEnum}}{{# isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}, {{/allParams}}configure: HttpRequestBuilder.() -> Unit = {}): Flow<{{{vendorExtensions.x-streaming-return}}}>
    {{/vendorExtensions.x-streaming}}
    {{/operation}}
}
{{/operations}}

{{#operations}}
fun {{classname}}(client: HttpClient, config: Config): {{apiPackage}}.{{classname}} = object : {{apiPackage}}.{{classname}} {
    {{#operation}}
    override suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{#isEnum}}{{# isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}},{{/allParams}} configure: HttpRequestBuilder.() -> Unit): {{#returnProperty}}{{#isFile}}HttpResponse{{/isFile}}{{^isFile}}{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{/isFile}}{{/returnProperty}} =
      client.request {
        configure()
        method = HttpMethod.{{#lambda.capitalised}}{{httpMethod}}{{/lambda.capitalised}}
        {{#headerParams}}
            header("{{baseName}}", {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}})
        {{/headerParams}}
        contentType(ContentType.Application.Json)
        {{#queryParams}}
            parameter("{{baseName}}", {{#isContainer}}{{{paramName}}}{{/isContainer}}{{^isContainer}}{{{paramName}}}{{/isContainer}})
        {{/queryParams}}
        url { path("{{#lambda.dropslash}}{{path}}{{/lambda.dropslash}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}"${{{paramName}}}"{{/isContainer}}){{/pathParams}}) }
        setBody({{#hasBodyParam}}{{#bodyParam}}{{#isArray}}{{operationIdCamelCase}}Request({{{paramName}}}{{^isList}}.asList(){{/isList}}){{/isArray}}{{^isArray}}{{#isMap}}{{operationIdCamelCase}}Request({{{paramName}}}){{/isMap}}{{^isMap}}{{{paramName}}}{{/isMap}}{{/isArray}}{{/bodyParam}}{{/hasBodyParam}}
        {{^hasBodyParam}}
            {{#hasFormParams}}
                {{#isMultipart}}
                    formData {
                    {{#formParams}}
                        {{#isArray}}
                            {{{paramName}}}?.onEach { appendGen("{{{baseName}}}[]", it) }
                        {{/isArray}}
                        {{^isArray}}
                            {{{paramName}}}?.apply { appendGen("{{{baseName}}}", {{{paramName}}}) }
                        {{/isArray}}
                    {{/formParams}}
                    }
                {{/isMultipart}}
                {{^isMultipart}}
                    ParametersBuilder().also {
                    {{#formParams}}
                        {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}.toString()) }
                    {{/formParams}}
                    }.build()
                {{/isMultipart}}
            {{/hasFormParams}}
            {{^hasFormParams}}
                io.ktor.client.utils.EmptyContent
            {{/hasFormParams}}
        {{/hasBodyParam}})
      }{{#returnProperty}}{{^isFile}}.serializeOrThrowWithResponseInfo(){{/isFile}}{{/returnProperty}}

    {{#vendorExtensions.x-streaming}}
    override fun {{operationId}}Stream({{#allParams}}{{{paramName}}}: {{#isEnum}}{{# isContainer}}kotlin.collections.List<{{enumName}}{{operationIdCamelCase}}>{{/isContainer}}{{^isContainer}}{{enumName}}{{operationIdCamelCase}}{{/isContainer}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#required}}{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{^isNumber}}{{#isEnum}}{{enumName}}{{operationIdCamelCase}}.{{enumDefaultValue}}{{/isEnum}}{{^isEnum}}{{{defaultValue}}}{{/isEnum}}{{/isNumber}}{{#isNumber}}{{{defaultValue}}}.toDouble(){{/isNumber}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}, {{/allParams}}configure: HttpRequestBuilder.() -> Unit): Flow<{{{vendorExtensions.x-streaming-return}}}> = flow {
      client.prepareRequest {
        timeout {
          requestTimeoutMillis = 60.seconds.toLong(DurationUnit.MILLISECONDS)
          socketTimeoutMillis = 60.seconds.toLong(DurationUnit.MILLISECONDS)
        }
        configure()
        method = HttpMethod.{{#lambda.capitalised}}{{httpMethod}}{{/lambda.capitalised}}
        accept(ContentType.Text.EventStream)
        header(HttpHeaders.CacheControl, "no-cache")
        header(HttpHeaders.Connection, "keep-alive")
        {{#headerParams}}
        header("{{baseName}}", {{#isContainer}}this.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}this.toString(){{/isContainer}})
        {{/headerParams}}
        contentType(ContentType.Application.Json)
        {{#queryParams}}
            parameter("{{baseName}}", {{#isContainer}}toMultiValue(this, "{{collectionFormat}}"){{/isContainer}}{{^isContainer}}listOf("${{{paramName}}}"){{/isContainer}})
        {{/queryParams}}
        url { path("{{#lambda.dropslash}}{{path}}{{/lambda.dropslash}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", {{#isContainer}}{{paramName}}.joinToString(","){{/isContainer}}{{^isContainer}}"${{{paramName}}}"{{/isContainer}}){{/pathParams}}) }
        {{#hasBodyParam}}
        val element = Json.encodeToJsonElement({{#lambda.serializer}}{{#bodyParam}}{{{baseName}}}{{/bodyParam}}{{/lambda.serializer}}, {{#bodyParam}}{{{paramName}}}{{/bodyParam}})
        val jsObject = JsonObject(element.jsonObject + Pair("stream", JsonPrimitive(true)))
        setBody(jsObject)
        {{/hasBodyParam}}
        {{^hasBodyParam}}
            {{#hasFormParams}}
                {{#isMultipart}}
                    formData {
                    {{#formParams}}
                        {{#isArray}}
                            {{{paramName}}}?.onEach { appendGen("{{{baseName}}}[]", it) }
                        {{/isArray}}
                        {{^isArray}}
                            {{{paramName}}}?.apply { appendGen("{{{baseName}}}", {{{paramName}}}) }
                        {{/isArray}}
                    {{/formParams}}
                    }
                {{/isMultipart}}
                {{^isMultipart}}
                    ParametersBuilder().also {
                    {{#formParams}}
                        {{{paramName}}}?.apply { it.append("{{{baseName}}}", {{{paramName}}}.toString()) }
                    {{/formParams}}
                    }.build()
                {{/isMultipart}}
            {{/hasFormParams}}
        {{/hasBodyParam}}
      }.execute { streamEvents(it, config.json, config.streamingPrefix, config.streamingDelimiter) }
    }
    {{/vendorExtensions.x-streaming}}
{{/operation}}
}
{{/operations}}
