package {{ sdk.namespace | caseDot }}.services

import android.net.Uri
import {{ sdk.namespace | caseDot }}.Client
import {{ sdk.namespace | caseDot }}.Service
{% if spec.definitions is not empty %}
import {{ sdk.namespace | caseDot }}.models.*
{% endif %}
{% if spec.enums is not empty %}
import {{ sdk.namespace | caseDot }}.enums.*
{% endif %}
import {{ sdk.namespace | caseDot }}.exceptions.{{ spec.title | caseUcfirst }}Exception
import {{ sdk.namespace | caseDot }}.extensions.classOf
{% if service.features.webAuth %}
import {{ sdk.namespace | caseDot }}.WebAuthComponent
import androidx.activity.ComponentActivity
{% endif %}
import okhttp3.Cookie
{% if (service.features.webAuth or service.features.location) %}
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
{% endif %}
import java.io.File

/**
 * {{ service.description | raw | replace({"\n": "", "\r": ""}) }}
**/
class {{ service.name | caseUcfirst }}(client: Client) : Service(client) {

{% for method in service.methods %}
    /**
     * {{ method.title }}
     *
     * {{ method.description | raw | replace({"\n": "", "\r": ""}) }}
     *
    {%~ for parameter in method.parameters.all %}
     * @param {{ parameter.name | caseCamel }} {{ parameter.description | raw }}
    {%~ endfor %}
    {%~ if method.type != "webAuth" %}
     * @return [{{ method | returnType(spec, sdk.namespace | caseDot) | raw }}]
    {%~ endif %}
     */
    {%~ if method.parameters.all | reduce((carry, param) => carry or not param.required) %}
    @JvmOverloads
    {%~ endif %}
    suspend fun {% if method.responseModel | hasGenericType(spec) %}{{ '<T>' | raw }} {% endif %}{{ method.name | caseCamel }}(
        {%~ if method.type == "webAuth" %}
        activity: ComponentActivity,
        {%~ endif %}
        {%~ for parameter in method.parameters.all %}
        {{ parameter.name | caseCamel }}: {{ parameter | typeName }}{%~ if not parameter.required or parameter.nullable %}? = null{% endif %},
        {%~ endfor %}
        {%~ if method.responseModel | hasGenericType(spec) %}
        nestedType: Class<T>,
        {%~ endif %}
        {%~ if 'multipart/form-data' in method.consumes %}
        onProgress: ((UploadProgress) -> Unit)? = null
        {%~ endif %}
    ){% if method.type != "webAuth" %}: {{ method | returnType(spec, sdk.namespace | caseDot) | raw }}{% endif %} {
        val apiPath = "{{ method.path }}"
        {%~ for parameter in method.parameters.path %}
            .replace("{{ '{' ~ parameter.name | caseCamel ~ '}' }}", {{ parameter.name | caseCamel }}{% if parameter.enumValues is not empty %}.value{% endif %})
        {%~ endfor %}

        val apiParams = mutableMapOf<String, Any?>(
        {%~ for parameter in method.parameters.query | merge(method.parameters.body) %}
            "{{ parameter.name }}" to {{ parameter.name | caseCamel }},
        {%~ endfor %}
        {%~ if method.type == 'location' or method.type == 'webAuth' %}
        {%~ if method.auth | length > 0 %}
        {%~ for node in method.auth %}
        {%~ for key,header in node | keys %}
            "{{ header | caseLower }}" to client.config["{{ header | caseLower }}"],
        {%~ endfor %}
        {%~ endfor %}
        {%~ endif %}
        {%~ endif %}
        )
        {%~ if method.type == 'webAuth' %}
        val apiQuery = mutableListOf<String>()
        apiParams.forEach {
            when (it.value) {
                null -> {
                    return@forEach
                }
                is List<*> -> {
                    (it.value as List<*>).forEach { v ->
                        apiQuery.add("${it.key}[]=${v.toString()}")
                    }
                }
                else -> {
                   apiQuery.add("${it.key}=${it.value.toString()}")
                }
            }
        }

        val apiUrl = Uri.parse("${client.endpoint}${apiPath}?${apiQuery.joinToString("&")}")
        val callbackUrlScheme = "{{ spec.title | caseLower }}-callback-${client.config["project"]}"

        WebAuthComponent.authenticate(activity, apiUrl, callbackUrlScheme) {
            if (it.isFailure) {
                throw it.exceptionOrNull()!!
            }

            val resultUrl = it.getOrNull()!!
            val uri = Uri.parse(resultUrl)
            val key = uri.getQueryParameter("key")
            val secret = uri.getQueryParameter("secret")
            if (key == null || secret == null) {
                throw {{ spec.title | caseUcfirst }}Exception("Authentication cookie missing!")
            }
            val cookie = Cookie.Builder()
                .name(key)
                .value(secret)
                .domain(Uri.parse(client.endpoint).host!!)
                .httpOnly()
                .build()
            
            client.http.cookieJar.saveFromResponse(
                client.endpoint.toHttpUrl(),
                listOf(cookie)
            )
        }
        {%~ elseif method.type == 'location' %}
        return client.call(
            "{{ method.method | caseUpper }}",
            apiPath,
            params = apiParams,
            responseType = {{ method | returnType(spec, sdk.namespace | caseDot) | raw }}::class.java
        )
        {%~ else %}
        val apiHeaders = mutableMapOf(
        {%~ for key, header in method.headers %}
            "{{ key }}" to "{{ header }}",
        {%~ endfor %}
        )
        {%~ if method.responseModel %}
        val converter: (Any) -> {{ method | returnType(spec, sdk.namespace | caseDot) | raw }} = {
        {%~ if method.responseModel == 'any' %}
            it
        {%~ else %}
            @Suppress("UNCHECKED_CAST")
            {{sdk.namespace | caseDot}}.models.{{ method.responseModel | caseUcfirst }}.from(map = it as Map<String, Any>{% if method.responseModel | hasGenericType(spec) %}, nestedType{% endif %})
        {%~ endif %}
        }
        {%~ endif %}
        {%~ if 'multipart/form-data' in method.consumes %}
        val idParamName: String? = {% if method.parameters.all | filter(p => p.isUploadID) | length > 0 %}{% for parameter in method.parameters.all | filter(parameter => parameter.isUploadID) %}"{{ parameter.name }}"{% endfor %}{% else %}null{% endif %}
    
        {%~ for parameter in method.parameters.all %}
        {%~ if parameter.type == 'file' %}
        val paramName = "{{ parameter.name }}"
        {%~ endif %}
        {%~ endfor %}
        return client.chunkedUpload(
            apiPath,
            apiHeaders,
            apiParams,
            responseType = {{ method | returnType(spec, sdk.namespace | caseDot) | raw }}::class.java,
            {%~ if method.responseModel %}
            converter,
            {%~ endif %}
            paramName,
            idParamName,
            onProgress,
        )
        {%~ else %}
        return client.call(
            "{{ method.method | caseUpper }}",
            apiPath,
            apiHeaders,
            apiParams,
            {%~ if method.responseModel | hasGenericType(spec) %}
            responseType = classOf(),
            {%~ else %}
            responseType = {{ method | returnType(spec, sdk.namespace | caseDot) | raw }}::class.java,
            {%~ endif %}
            {%~ if method.responseModel %}
            converter,
            {%~ endif %}
        )
        {%~ endif %}
        {%~ endif %}
    }

    {%~ if method.responseModel | hasGenericType(spec) %}
    /**
     * {{ method.title }}
     *
     * {{ method.description | raw | replace({"\n": "", "\r": ""}) }}
     *
    {%~ for parameter in method.parameters.all %}
     * @param {{ parameter.name | caseCamel }} {{ parameter.description | raw }}
    {%~ endfor %}
    {%~ if method.type != "webAuth" %}
     * @return [{{ method | returnType(spec, sdk.namespace | caseDot) | raw }}]
    {%~ endif %}
     */
    {%~ if method.parameters.all | reduce((carry, param) => carry or not param.required) %}
    @JvmOverloads
    {%~ endif %}
    @Throws({{ spec.title | caseUcfirst }}Exception::class)
    suspend fun {{ method.name | caseCamel }}(
        {%~ if method.type == "webAuth" %}
        activity: ComponentActivity,
        {%~ endif %}
        {%~ for parameter in method.parameters.all %}
        {{ parameter.name | caseCamel }}: {{ parameter | typeName }}{%~ if not parameter.required or parameter.nullable %}? = null{% endif %},
        {%~ endfor %}
        {%~ if 'multipart/form-data' in method.consumes %}
        onProgress: ((UploadProgress) -> Unit)? = null
        {%~ endif %}
    ): {{ method | returnType(spec, sdk.namespace | caseDot, 'Map<String, Any>') | raw }} = {{ method.name | caseCamel }}(
        {%~ if method.type == "webAuth" %}
        activity,
        {%~ endif %}
        {%~ for parameter in method.parameters.all %}
        {{ parameter.name | caseCamel }},
        {%~ endfor %}
        {%~ if method.responseModel | hasGenericType(spec) %}
        nestedType = classOf(),
        {%~ endif %}
        {%~ if 'multipart/form-data' in method.consumes %}
        onProgress = onProgress
        {%~ endif %}
    )
    {%~ endif %}

    {%~ endfor %}
}