package com.example.modifier.http

import android.util.Log
import com.example.modifier.Utils
import com.example.modifier.data.AppPreferencesRepository
import com.example.modifier.http.response.ErrorResponse
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.network.sockets.ConnectTimeoutException
import io.ktor.client.network.sockets.SocketTimeoutException
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.HttpRequestTimeoutException
import io.ktor.client.plugins.HttpResponseValidator
import io.ktor.client.plugins.HttpSend
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.resources.Resources
import io.ktor.client.utils.unwrapCancellationException
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json

private fun Throwable.isTimeoutException(): Boolean {
    val exception = unwrapCancellationException()
    return exception is HttpRequestTimeoutException ||
            exception is ConnectTimeoutException ||
            exception is SocketTimeoutException
}

@OptIn(ExperimentalSerializationApi::class)
fun ktorClient(baseUrl: String) = HttpClient(OkHttp) {
    defaultRequest {
        url(baseUrl.endsWith("/").let {
            if (it) baseUrl else "$baseUrl/"
        })
    }
    install(HttpSend) {
        maxSendCount = 200
    }
    install(Logging) {
        level = LogLevel.INFO
    }
    install(HttpRequestRetry) {
        maxRetries = 3
        retryOnExceptionIf { _, cause ->
            when {
                cause.isTimeoutException() -> true
                cause is CancellationException -> false
                else -> true
            }
        }
        retryIf { _, response ->
            response.status.value.let {
                when (it) {
                    425, 429, 502, 503, 504 -> true
                    else -> false
                }
            }
        }
    }
    install(HttpTimeout) {
        requestTimeoutMillis = 30 * 1000
        socketTimeoutMillis = 30 * 1000
    }
    install(Resources)
    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
            ignoreUnknownKeys = true
            explicitNulls = false
        })
    }
    HttpResponseValidator {
        validateResponse { response ->
            if (response.status.value !in 200..299) {
                val error = response.body<ErrorResponse>()
                throw ClientRequestException(response, error.message ?: "Unknown error")
            }
        }
    }
}