package com.sheep.gamegroup.model.util;

import android.text.TextUtils;

import com.sheep.gamegroup.model.api.BaseMessageConverter;
import com.sheep.gamegroup.util.StringUtils;

import java.io.EOFException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpHeaders;
import okio.Buffer;
import okio.BufferedSource;
import okio.GzipSource;

/**
 * Created by realicing on 2018/7/20.
 * realicing@sina.com
 */
public class LogInterceptor implements Interceptor {
    private static final Charset UTF8 = Charset.forName("UTF-8");
    private final LogInterceptor.ILogger logger;
    private volatile LogInterceptor.ELevel level;
    private boolean isDecrypt = false;
    public LogInterceptor(boolean isDecrypt) {
        this(isDecrypt, LogInterceptor.ILogger.DEFAULT);
    }

    public LogInterceptor(boolean isDecrypt, LogInterceptor.ILogger logger) {
        this.level = LogInterceptor.ELevel.NONE;
        this.logger = logger;
        this.isDecrypt = isDecrypt;
    }

    public LogInterceptor setLevel(LogInterceptor.ELevel level) {
        if (level == null) {
            throw new NullPointerException("level == null. Use Level.NONE instead.");
        } else {
            this.level = level;
            return this;
        }
    }

    public LogInterceptor.ELevel getLevel() {
        return this.level;
    }

    public Response intercept(Chain chain) throws IOException {
        LogInterceptor.ELevel level = this.level;
        Request request = chain.request();
        if (level == LogInterceptor.ELevel.NONE) {
            return chain.proceed(request);
        } else {
            boolean logBody = level == LogInterceptor.ELevel.BODY;
            boolean logHeaders = logBody || level == LogInterceptor.ELevel.HEADERS;
            RequestBody requestBody = request.body();
            boolean hasRequestBody = requestBody != null;
            Connection connection = chain.connection();
            String requestStartMessage = "--> " + request.method() + ' ' + request.url() + (connection != null ? " " + connection.protocol() : "");
            if (!logHeaders && hasRequestBody) {
                requestStartMessage = requestStartMessage + " (" + requestBody.contentLength() + "-byte body)";
            }

            this.logger.log(requestStartMessage);
            if (logHeaders) {
                if (hasRequestBody) {
                    if (requestBody.contentType() != null) {
                        this.logger.log("Content-Type: " + requestBody.contentType());
                    }

                    if (requestBody.contentLength() != -1L) {
                        this.logger.log("Content-Length: " + requestBody.contentLength());
                    }
                }

                Headers headers = request.headers();
                int i = 0;

                for(int count = headers.size(); i < count; ++i) {
                    String name = headers.name(i);
                    if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
                        this.logger.log(name + ": " + headers.value(i));
                    }
                }

                if (logBody && hasRequestBody) {
                    if (this.bodyHasUnknownEncoding(request.headers())) {
                        this.logger.log("--> END " + request.method() + " (encoded body omitted)");
                    } else {
                        Buffer buffer = new Buffer();
                        requestBody.writeTo(buffer);
                        Charset charset = null;
                        MediaType contentType = requestBody.contentType();
                        if (contentType != null) {
                            charset = contentType.charset(UTF8);
                        }
                        if(charset == null)
                            charset = UTF8;

                        this.logger.log("");
                        if (isPlaintext(buffer)) {
                            String result = buffer.readString(charset);
                            String json = BaseMessageConverter.decrypt(result);
                            this.logger.log(isDecrypt && !TextUtils.equals(result, json) ? "\n" + result + "\n" + StringUtils.parseJson(json) : "\n" + StringUtils.parseJson(json));
                            this.logger.log("--> END " + request.method() + " (" + requestBody.contentLength() + "-byte body)");
                        } else {
                            this.logger.log("--> END " + request.method() + " (binary " + requestBody.contentLength() + "-byte body omitted)");
                        }
                    }
                } else {
                    this.logger.log("--> END " + request.method());
                }
            }

            long startNs = System.nanoTime();

            Response response;
            try {
                response = chain.proceed(request);
            } catch (Exception var27) {
                this.logger.log("<-- HTTP FAILED: " + var27);
                throw var27;
            }

            long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
            ResponseBody responseBody = response.body();
            long contentLength = responseBody.contentLength();
            String bodySize = contentLength != -1L ? contentLength + "-byte" : "unknown-length";
            this.logger.log("<-- " + response.code() + (response.message().isEmpty() ? "" : ' ' + response.message()) + ' ' + response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", " + bodySize + " body" : "") + ')');
            if (logHeaders) {
                Headers headers = response.headers();
                int i = 0;

                for(int count = headers.size(); i < count; ++i) {
                    this.logger.log(headers.name(i) + ": " + headers.value(i));
                }

                if (logBody && HttpHeaders.hasBody(response)) {
                    if (this.bodyHasUnknownEncoding(response.headers())) {
                        this.logger.log("<-- END HTTP (encoded body omitted)");
                    } else {
                        BufferedSource source = responseBody.source();
                        source.request(9223372036854775807L);
                        Buffer buffer = source.buffer();
                        Long gzippedLength = null;
                        if ("gzip".equalsIgnoreCase(headers.get("Content-Encoding"))) {
                            gzippedLength = buffer.size();
                            GzipSource gzippedResponseBody = null;

                            try {
                                gzippedResponseBody = new GzipSource(buffer.clone());
                                buffer = new Buffer();
                                buffer.writeAll(gzippedResponseBody);
                            } finally {
                                if (gzippedResponseBody != null) {
                                    gzippedResponseBody.close();
                                }

                            }
                        }

                        Charset charset = null;
                        MediaType contentType = responseBody.contentType();
                        if (contentType != null) {
                            charset = contentType.charset(UTF8);
                        }
                        if(charset == null)
                            charset = UTF8;

                        if (!isPlaintext(buffer)) {
                            this.logger.log("");
                            this.logger.log("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
                            return response;
                        }

                        if (contentLength != 0L) {
                            this.logger.log("");
                            String result = buffer.clone().readString(charset);
                            String json = BaseMessageConverter.decrypt(result);
                            this.logger.log(isDecrypt && !TextUtils.equals(result, json) ? "\n" + result + "\n" + StringUtils.parseJson(json) : "\n" + StringUtils.parseJson(json));
                        }

                        if (gzippedLength != null) {
                            this.logger.log("<-- END HTTP (" + buffer.size() + "-byte, " + gzippedLength + "-gzipped-byte body)");
                        } else {
                            this.logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
                        }
                    }
                } else {
                    this.logger.log("<-- END HTTP");
                }
            }

            return response;
        }
    }

    static boolean isPlaintext(Buffer buffer) {
        try {
            Buffer prefix = new Buffer();
            long byteCount = buffer.size() < 64L ? buffer.size() : 64L;
            buffer.copyTo(prefix, 0L, byteCount);

            for(int i = 0; i < 16 && !prefix.exhausted(); ++i) {
                int codePoint = prefix.readUtf8CodePoint();
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false;
                }
            }

            return true;
        } catch (EOFException var6) {
            return false;
        }
    }

    private boolean bodyHasUnknownEncoding(Headers headers) {
        String contentEncoding = headers.get("Content-Encoding");
        return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity") && !contentEncoding.equalsIgnoreCase("gzip");
    }

    //规定每段显示的长度
    private static int LOG_MAXLENGTH = 2000;
    public interface ILogger {
        LogInterceptor.ILogger DEFAULT = new LogInterceptor.ILogger() {
            public void log(String message) {

                int strLength = message.length();
                int start = 0;
                int end = LOG_MAXLENGTH;
                for (int i = 0; i < 100; i++) {
                    //剩下的文本还是大于规定长度则继续重复截取并输出
                    if (strLength > end) {
                        Logger.getLogger("sheepLog").log(Level.INFO, message.substring(start, end), (Throwable) null);
                        start = end;
                        end = end + LOG_MAXLENGTH;
                    } else {
                        Logger.getLogger("sheepLog").log(Level.INFO, message.substring(start, strLength), (Throwable) null);
                        break;
                    }
                }
            }
        };

        void log(String var1);
    }

    public static enum ELevel {
        NONE,
        BASIC,
        HEADERS,
        BODY;

        private ELevel() {
        }
    }
}
