package com.kfzs.duanduan.utils.net;

import android.text.TextUtils;

import com.android.volleyplus.AuthFailureError;
import com.android.volleyplus.DefaultRetryPolicy;
import com.android.volleyplus.Request;
import com.android.volleyplus.Response;
import com.android.volleyplus.request.RequestOption;
import com.kfzs.duanduan.KFZSApp;
import com.kfzs.duanduan.bll.UrlBll;
import com.kfzs.duanduan.db.DataSave;
import com.kfzs.duanduan.datashare.DDProviderHelper;
import com.kfzs.duanduan.bean.BaseMsg;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * <pre>
 *     sinlov
 *
 *     /\__/\
 *    /`    '\
 *  ≈≈≈ 0  0 ≈≈≈ Hello world!
 *    \  --  /
 *   /        \
 *  /          \
 * |            |
 *  \  ||  ||  /
 *   \_oo__oo_/≡≡≡≡≡≡≡≡o
 *
 * </pre>
 * Created by sinlov on 17/2/17.
 */
public final class APIRequestOptionV2 {
    /**
     * for get set.
     */
    public static final int GET = 1;
    /**
     * for post set.
     */
    public static final int POST = 2;
    /**
     * for put set.
     */
    public static final int PUT = 3;

    private final Object tag;
    private final int category;
    private final int initialTimeoutMs;
    private final int maxNumRetries;
    private final float backoffMultiplier;
    private final String url;
    private final Map<String, String> urlParams;
    private final Map<String, String> headParams;
    private final Map<String, String> bodyParams;
    private final Response.Listener<BaseMsg> responseListener;
    private final Response.ErrorListener errorListener;
    private APIRequestV2 APIRequestV2;
    private DefaultRetryPolicy defaultRetryPolicy;
    private final byte[] bodyByte;
    private StringBuffer sb;

    private APIRequestV2 postAPIRequestV2(String url,
                                          Response.Listener<BaseMsg> responseListener, Response.ErrorListener errorListener, final Map<String, String> body, byte[] bodyByte) {
        String apiUrl = matchURL(url);
        APIRequestV2 result = new APIRequestV2(Request.Method.POST, apiUrl, bodyByte, responseListener, errorListener) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> map=super.getHeaders();
                if(headParams!=null)
                map.putAll(headParams);
                return map;
            }

            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                return body;
            }

        };
        result.setTag(tag);
        result.setRetryPolicy(defaultRetryPolicy);
        return result;
    }

    private APIRequestV2 getAPIRequestV2(String url,
                                         Response.Listener<BaseMsg> responseListener, Response.ErrorListener errorListener, final Map<String, String> headParams, byte[] bodyByte) {
        String apiUrl = matchURL(url);
        APIRequestV2 result = new APIRequestV2(Request.Method.GET, apiUrl, bodyByte, responseListener, errorListener) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> map=super.getHeaders();
                if(headParams!=null)
                map.putAll(headParams);
                return map;
            }
        };
        result.setTag(tag);
        result.setRetryPolicy(defaultRetryPolicy);
        return result;
    }

    private APIRequestV2 putAPIRequestV2(String url,
                                         Response.Listener<BaseMsg> responseListener, Response.ErrorListener errorListener, final Map<String, String> body, byte[] bodyByte) {
        String apiUrl = matchURL(url);
        APIRequestV2 result = new APIRequestV2(Request.Method.PUT, apiUrl, bodyByte, responseListener, errorListener) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> map=super.getHeaders();
                if(headParams!=null)
                map.putAll(headParams);
                return map;
            }

            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                return body;
            }
        };
        result.setTag(tag);
        result.setRetryPolicy(defaultRetryPolicy);
        return result;
    }

    private APIRequestOptionV2(Builder builder) {
        category = builder.category;
        tag = builder.tag;
        initialTimeoutMs = builder.initialTimeoutMs;
        maxNumRetries = builder.maxNumRetries;
        backoffMultiplier = builder.backoffMultiplier;

        url = builder.url;
        responseListener = builder.responseListener;
        errorListener = builder.errorListener;

        urlParams = builder.urlParams;
        if (null == builder.headParams) {
            headParams = new HashMap<String, String>();
        } else {
            headParams = builder.headParams;
        }
        headParams.put(UrlBll.HeadParams.SECRET_KEY, DataSave.getInstance().getApkSign());
        addUserTokenDefault();
//        headParams.put(UrlBll.HeadParams.DD_CHANNEL, DataSave.getInstance().getChannelName());
        bodyParams = builder.bodyParams;
        bodyByte = builder.bodyByte;
    }

    /**
     * 所有请求都加头
     */
    private void addUserTokenDefault() {
        String token = DDProviderHelper.getInstance().getLastUserToken(KFZSApp.context);
        if (TextUtils.isEmpty(token)) {
            return;
        }
        headParams.put(UrlBll.HeadParams.AUTHORIZATION, token);
    }

    private String matchURL(String head) {
        if (null == urlParams) {
            return head;
        }
        if (sb == null) {
            sb = new StringBuffer();
        }
        sb.setLength(0);
        sb.append(head);
        sb.append("?");
        try {
            for (Map.Entry<String, String> param : urlParams.entrySet()) {
                sb.append(param.getKey());
                sb.append("=");
                String value = param.getValue();
                sb.append(URLEncoder.encode(value, "UTF-8"));
                sb.append("&");
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            sb.setLength(0);
            sb.append(head);
            sb.append("?");
            for (Map.Entry<String, String> param : urlParams.entrySet()) {
                sb.append(param.getKey());
                sb.append("=");
                sb.append(param.getValue());
                sb.append("&");
            }
        }
        sb.setLength(sb.length() - 1);
        return sb.toString();
    }

    /**
     * for build {@link APIRequestV2}
     * <p>
     * You must set like this <br>
     * {@link #setCategory(int)} <br>
     * {@link #setUrl(String)} <br>
     * {@link #setErrorListener(Response.ErrorListener)} <br>
     * <br>
     * you can use to set more thins <br>
     * {@link #setTag(Object)} <br>
     * {@link #setInitialTimeoutMs(int)}  <br>
     * {@link #setMaxNumRetries(int)}  <br>
     * {@link #setBackoffMultiplier(float)}   <br>
     */
    public static class Builder {

        public static final int INITIAL_TIMEOUT_MILLISECOND = 2000;
        public static final int MAX_NUM_RETRIES = 1;
        public static final float BACK_OFF_MULTIPLIER = 1.0F;
        private int category = 0;
        private Object tag = RequestOption.DEFAULT_REQ_TAG;
        private int initialTimeoutMs = INITIAL_TIMEOUT_MILLISECOND;
        private int maxNumRetries = MAX_NUM_RETRIES;
        private float backoffMultiplier = BACK_OFF_MULTIPLIER;

        private String url = "";
        private Response.Listener<BaseMsg> responseListener = null;
        private Response.ErrorListener errorListener = null;

        private Map<String, String> urlParams = null;
        private Map<String, String> headParams = null;
        private Map<String, String> bodyParams = null;
        private byte[] bodyByte;

        /**
         * you can use like this
         * <br><li>{@link #POST}</li>
         * <br><li>{@link #GET}</li>
         * <br><li>{@link #PUT}</li>
         * <br> If not set will throw exception
         *
         * @param category see {@link APIRequestOptionV2}
         * @return this
         */
        public Builder setCategory(int category) {
            this.category = category;
            return this;
        }

        public Builder setTag(Object tag) {
            this.tag = tag;
            return this;
        }

        public Builder setInitialTimeoutMs(int initialTimeoutMs) {
            this.initialTimeoutMs = initialTimeoutMs;
            return this;
        }

        public Builder setMaxNumRetries(int maxNumRetries) {
            this.maxNumRetries = maxNumRetries;
            return this;
        }

        public Builder setBackoffMultiplier(float backoffMultiplier) {
            this.backoffMultiplier = backoffMultiplier;
            return this;
        }

        public Builder setUrl(String url) {
            this.url = url;
//            if (KFApplication.getInstance().isCoreDebug()) {
//                this.url = url.replace("z.kuaifazs.com", "test.z.kuaifazs.com");
//            }
//            if (KFApplication.getInstance().isStatisticDebug()) {
//                this.url = url.replace("d.kuaifazs.com", "test.d.kuaifazs.com");
//            }
            return this;
        }

        /**
         * Set Url Params it will match like this
         * <pre>
         *     Http://xxx.xxx.com/getFunctionGroup?m_id=6&ware_id=2?m_id=6&ware_id=2
         * </pre>
         * if use this way url set before "?".
         *
         * @param urlParams {@link Map}
         */
        public Builder setUrlParams(Map<String, String> urlParams) {
            this.urlParams = urlParams;
            return this;
        }

        /**
         * 已经自动加了渠道头和token头
         *
         * @param headParams
         * @return
         */
        public Builder addHeadParams(Map<String, String> headParams) {
            String[] sets = headParams.keySet().toArray(new String[]{});
            for (String set : sets) {
                if (!this.headParams.containsKey(set)) {
                    this.headParams.remove(set);
                }
                this.headParams.put(set, headParams.get(set));
            }
            return this;
        }

        /**
         * set body params
         *
         * @param bodyParams {@link Map}
         */
        public Builder setBodyParams(Map<String, String> bodyParams) {
            this.bodyParams = bodyParams;
            return this;
        }

        public Builder setBodyByte(byte[] bodyByte) {
            this.bodyByte = bodyByte;
            return this;
        }

        public Builder setResponseListenerV2(Response.Listener<BaseMsg> responseListener) {
            this.responseListener = responseListener;
            return this;
        }

        public Builder setErrorListener(Response.ErrorListener errorListener) {
            this.errorListener = errorListener;
            return this;
        }

        public APIRequestOptionV2 build() {
            return new APIRequestOptionV2(this);
        }
    }

    /**
     * /**
     * It will throw exception at info head like
     * {@link RequestOption#ERROR_MustSet_params}<br>
     * or {@link RequestOption#ERROR_CATEGORY}
     *
     * @return {@link APIRequestV2}
     */
    public APIRequestV2 create() {
        if (null == url) {
            new Throwable(RequestOption.ERROR_MustSet_params + "url").printStackTrace();
        }
        //去旧无用的Null校验
//        if (null == responseListener) {
//            new Throwable(RequestOption.ERROR_MustSet_params + "responseListener").printStackTrace();
//        }
//        if (null == errorListener) {
//            new Throwable(RequestOption.ERROR_MustSet_params + "errorListener").printStackTrace();
//        }
        defaultRetryPolicy = new DefaultRetryPolicy(initialTimeoutMs, maxNumRetries, backoffMultiplier);
        switch (category) {
            case POST:
                APIRequestV2 = postAPIRequestV2(url, responseListener, errorListener, bodyParams, bodyByte);
                break;
            case GET:
                APIRequestV2 = getAPIRequestV2(url, responseListener, errorListener, headParams, bodyByte);
                break;
            case PUT:
                APIRequestV2 = putAPIRequestV2(url, responseListener, errorListener, bodyParams, bodyByte);
                break;
            default:
                new Throwable(RequestOption.ERROR_CATEGORY).printStackTrace();
                break;
        }
        return APIRequestV2;
    }
}
