/*
 * Copyright (C) 2016 Jeff Gilfelt.
 * Copyright 2019 New Vector Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package im.vector.matrix.android.internal.network.interceptors

import im.vector.matrix.android.internal.di.MatrixScope
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import java.io.IOException
import java.nio.charset.Charset
import javax.inject.Inject

/**
 * An OkHttp interceptor that logs requests as curl shell commands. They can then
 * be copied, pasted and executed inside a terminal environment. This might be
 * useful for troubleshooting client/server API interaction during development,
 * making it easy to isolate and share requests made by the app. <p> Warning: The
 * logs generated by this interceptor have the potential to leak sensitive
 * information. It should only be used in a controlled manner or in a
 * non-production environment.
 */
@MatrixScope
internal class CurlLoggingInterceptor @Inject constructor(private val logger: HttpLoggingInterceptor.Logger)
    : Interceptor {

    /**
     * Set any additional curl command options (see 'curl --help').
     */
    var curlOptions: String? = null

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()

        var compressed = false

        var curlCmd = "curl"
        if (curlOptions != null) {
            curlCmd += " " + curlOptions!!
        }
        curlCmd += " -X " + request.method()

        val requestBody = request.body()
        if (requestBody != null) {
            val buffer = Buffer()
            requestBody.writeTo(buffer)
            var charset: Charset? = UTF8
            val contentType = requestBody.contentType()
            if (contentType != null) {
                charset = contentType.charset(UTF8)
            }
            // try to keep to a single line and use a subshell to preserve any line breaks
            curlCmd += " --data $'" + buffer.readString(charset!!).replace("\n", "\\n") + "'"
        }

        val headers = request.headers()
        var i = 0
        val count = headers.size()
        while (i < count) {
            val name = headers.name(i)
            val value = headers.value(i)
            if ("Accept-Encoding".equals(name, ignoreCase = true) && "gzip".equals(value, ignoreCase = true)) {
                compressed = true
            }
            curlCmd += " -H \"$name: $value\""
            i++
        }

        curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url().toString()
                // Replace localhost for emulator by localhost for shell
                .replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/")
                + "'")

        // Add Json formatting
        curlCmd += " | python -m json.tool"

        logger.log("--- cURL (" + request.url() + ")")
        logger.log(curlCmd)

        return chain.proceed(request)
    }

    companion object {
        private val UTF8 = Charset.forName("UTF-8")
    }
}
