/*
 * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.client.features

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.response.*
import io.ktor.util.*
import io.ktor.utils.io.core.*
import kotlin.jvm.*

private val ValidateMark = AttributeKey<Unit>("ValidateMark")

/**
 * Default response validation.
 * Check the response status code in range (0..299).
 */
fun HttpClientConfig<*>.addDefaultResponseValidation() {
    HttpResponseValidator {
        validateResponse { response ->
            val statusCode = response.status.value
            val originCall = response.call
            if (statusCode < 300 || originCall.attributes.contains(ValidateMark)) return@validateResponse

            response.use {
                val exceptionCall = originCall.save().apply {
                    attributes.put(ValidateMark, Unit)
                }

                val exceptionResponse = exceptionCall.response
                when (statusCode) {
                    in 300..399 -> throw RedirectResponseException(exceptionResponse)
                    in 400..499 -> throw ClientRequestException(exceptionResponse)
                    in 500..599 -> throw ServerResponseException(exceptionResponse)
                }

                if (statusCode >= 600) {
                    throw ResponseException(exceptionResponse)
                }
            }
        }
    }
}

/**
 * Base for default response exceptions.
 * @param response: origin response
 */
open class ResponseException(
    @Transient val response: HttpResponse
) : IllegalStateException("Bad response: $response")

/**
 * Unhandled redirect exception.
 */
@Suppress("KDocMissingDocumentation")
class RedirectResponseException(response: HttpResponse) : ResponseException(response) {
    override val message: String? = "Unhandled redirect: ${response.call.request.url}. Status: ${response.status}"
}

/**
 * Server error exception.
 */
@Suppress("KDocMissingDocumentation")
class ServerResponseException(
    response: HttpResponse
) : ResponseException(response) {
    override val message: String? = "Server error(${response.call.request.url}: ${response.status}."
}

/**
 * Bad client request exception.
 */
@Suppress("KDocMissingDocumentation")
class ClientRequestException(
    response: HttpResponse
) : ResponseException(response) {
    override val message: String? = "Client request(${response.call.request.url}) invalid: ${response.status}"
}
