package ${domain};

import ${domain}.frame.AESUtil;
import ${domain}.frame.ApiRequest;
import ${domain}.frame.ApiResponse;
import ${domain}.frame.ErrorType;
import ${domain}.frame.MD5Util;
import ${domain}.frame.MapperUtil;
import ${domain}.frame.ProgressRequestBody;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ApiClient {
    private static ApiClient ourInstance = null;

    public static void init(String serverUrl, String appKey, String appSecret) {
        init(serverUrl, appKey, appSecret, DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT);
    }

    public static void init(String serverUrl, String appKey, String appSecret, int connectTimeout, int readTimeout) {
        ourInstance = new ApiClient(serverUrl, appKey, appSecret, connectTimeout, readTimeout);
    }

    public static ApiClient getInstance() {
        if (ourInstance == null) {
            System.err.print("ApiClient need init");
        }
        return ourInstance;
    }

    //应用码
    private String appKey;
    //应用安全码
    private String appSecret;
    //服务器地址
    private String serverUrl;
    private OkHttpClient httpClient = null;
    //默认参数
    private static final int DEFAULT_CONNECT_TIMEOUT = 5;//秒
    private static final int DEFAULT_READ_TIMEOUT = 10;//秒

    private Before before = null;
    private After after = null;
    private String token = "";
    private boolean debug = false;

    private ApiClient(String serverUrl, String appKey, String appSecret, int connectTimeout, int readTimeout) {
        this.appKey = appKey;
        this.appSecret = appSecret;
        this.serverUrl = serverUrl;
        this.httpClient = new OkHttpClient.Builder()
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .build();
    }

    public interface Callback<T extends ApiResponse> {
        void call(T response);
    }

    public interface Before {
        void call(ApiRequest request);
    }

    public interface After {
        void call(ApiRequest request, ApiResponse response);
    }

    public void setAfter(After after) {
        this.after = after;
    }

    public void setBefore(Before before) {
        this.before = before;
    }

    public <T extends ApiResponse> T execute(ApiRequest<T> request) {
        return execute(request, null);
    }

    public <T extends ApiResponse> T execute(ApiRequest<T> request, ProgressRequestBody.ProgressListener listener) {
        if (before != null) {
            before.call(request);
        }

        if (debug) {
            System.out.println("debug-->请求内容:" + MapperUtil.toJson(request));
        }

        // 检查请求参数
        T t = MapperUtil.toJava("{}", request.responseClass());

        try {
            //装载请求参数
            String currentTime = String.valueOf(System.currentTimeMillis());
            String base64 = AESUtil.encrypt2Base64(MapperUtil.toJson(request).getBytes(), appSecret);

            RequestBody requestBody = new MultipartBody.Builder()
                    .addFormDataPart("appKey", appKey)
                    .addFormDataPart("encryptData", null, ProgressRequestBody.createProgressRequestBody(base64, listener))
                    .addFormDataPart("sign", sign(request, currentTime))
                    .addFormDataPart("timestamp", currentTime)
                    .addFormDataPart("token", token)
                    .build();

            Request build = new Request.Builder()
                    .url(serverUrl + request.path())
                    .post(requestBody)
                    .build();

            Response response = httpClient.newCall(build).execute();
            if (response.isSuccessful()) {
                String responseString = response.body().string();
                String responseJson = null;
                if (!responseString.startsWith("{")) {
                    if (debug) System.out.println("debug-->加密响应结果:" + responseString);
                    responseJson = AESUtil.decrypt2String(responseString, appSecret);
                    if (debug) System.out.println("debug-->响应结果:" + responseJson);
                } else {
                    responseJson = responseString;
                    if (debug) System.out.println("debug-->响应结果:" + responseJson);
                }
                t = MapperUtil.toJava(responseJson, request.responseClass());
            } else if (404 == response.code()) {
                t.addError(ErrorType.SYSTEM_ERROR, "地址不存在!");
            } else if (403 == response.code()) {
                t.addError(ErrorType.SYSTEM_ERROR, "禁止访问!");
            } else if (500 == response.code()) {
                t.addError(ErrorType.SYSTEM_ERROR, "服务器走了下神!");
            }
        } catch (ConnectException e) {
            t.addError(ErrorType.SYSTEM_ERROR, "网络异常!");
        } catch (SocketTimeoutException e) {
            t.addError(ErrorType.SYSTEM_ERROR, "请求超时!");
        } catch (IOException e) {
            t.addError(ErrorType.SYSTEM_ERROR, "请求异常!");
        } finally {
            if (after != null) {
                after.call(request, t);
            }
        }
        return t;
    }

    public <T extends ApiResponse> void executeCall(ApiRequest<T> request, Callback callback) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                T execute = execute(request, null);
                callback.call(execute);
            }
        });
    }

    public <T extends ApiResponse> void executeCall(ApiRequest<T> request, Callback callback, ProgressRequestBody.ProgressListener listener) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                T execute = execute(request, listener);
                callback.call(execute);
            }
        });
    }

    /**
     * 对请求进行签名
     *
     * @param request
     * @return
     */
    private String sign(ApiRequest request, String currentTime) {
        String json = MapperUtil.toJson(request);
        return MD5Util.encode(appSecret + json + currentTime);
    }

    public void setToken(String token) {
        this.token = token;
    }

    public boolean isDebug() {
        return debug;
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }
}

