package ru.fincode.tsudesk.core.network.interceptor

import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
import kotlin.math.min


class RetryInterceptor(
    private val maxAttempts: Int = 3,
    private val baseDelayMs: Long = 300L
) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()

        // На всякий: ретраим только идемпотентные методы
        val method = request.method.uppercase()
        val retryAllowed = method == "GET" || method == "HEAD"

        var attempt = 1
        var lastException: IOException? = null
        var lastResponse: Response? = null

        while (attempt <= maxAttempts) {
            // закрываем предыдущий response, чтобы не текли ресурсы
            lastResponse?.close()
            lastResponse = null

            try {
                val response = chain.proceed(request)

                if (!retryAllowed) return response

                if (!response.shouldRetry()) {
                    return response
                }

                // Нужно ретраить — подождём и попробуем снова
                if (attempt == maxAttempts) return response

                sleepBackoff(attempt)

                attempt++
                continue
            } catch (e: IOException) {
                lastException = e
                if (!retryAllowed || attempt == maxAttempts) throw e

                sleepBackoff(attempt)

                attempt++
            }
        }

        // Теоретически не должны попасть сюда
        lastResponse?.let { return it }
        lastException?.let { throw it }
        throw IOException("RetryInterceptor: exhausted attempts without response/exception")
    }

    private fun Response.shouldRetry(): Boolean {
        return when (code) {
            408, 429 -> true
            in 500..599 -> true
            else -> false
        }
    }

    private fun sleepBackoff(attempt: Int) {
        val delay = baseDelayMs * min(attempt.toLong(), 2L)
        try {
            Thread.sleep(delay)
        } catch (_: InterruptedException) {
        }
    }
}