package de.sebse.fuplanner.tools.network;

import android.content.Context;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.TimeoutError;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.Volley;

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

import androidx.annotation.Nullable;
import de.sebse.fuplanner.tools.EventListener;
import de.sebse.fuplanner.tools.logging.Logger;

/**
 * Created by sebastian on 24.10.17.
 */

public class HTTPService {
    private final RequestQueue requestQueue;
    private final Context mContext;
    protected final Logger log = new Logger(this);
    private final EventListener<VolleyError> errorResponseListener = new EventListener<>();
    private final EventListener<Result> successResponseListener = new EventListener<>();

    public HTTPService(Context context) {
        this.mContext = context;
        requestQueue = Volley.newRequestQueue(context, new BetterHurlStack(false));
    }

    public void addErrorListener(String id, EventListener.EventFunction<VolleyError> listener) {
        errorResponseListener.add(id, listener);
    }

    public void removeErrorListener(String id) {
        errorResponseListener.remove(id);
    }

    public void addSuccessListener(String id, EventListener.EventFunction<Result> listener) {
        successResponseListener.add(id, listener);
    }

    public void removeSuccessListener(String id) {
        successResponseListener.remove(id);
    }

    protected void get(String url, @Nullable final HashMap<String, String> cookies, Response.Listener<Result> response, Response.ErrorListener error) {
        HttpRequest request = new HttpRequest(Request.Method.GET, url, response, error) {
            @Override
            public void deliverError(VolleyError error) {
                if (error == null) {
                    deliver(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
                } else if (error.networkResponse == null) {
                    int statusCode;
                    if (error instanceof TimeoutError)
                        statusCode = 408;
                    else
                        statusCode = 500;
                    deliver(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
                } else {
                    final int status = error.networkResponse.statusCode;
                    if (status == 302) {
                        deliverResponse(new Result(null, error.networkResponse.headers));
                    } else {
                        deliver(error);
                    }
                }
            }

            @Override
            protected void deliverResponse(Result response) {
                successResponseListener.emit(response);
                super.deliverResponse(response);
            }

            private void deliver(VolleyError error) {
                errorResponseListener.emit(error);
                super.deliverError(error);
            }

            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> params = super.getHeaders();
                if (cookies != null) {
                    if (params==null)
                        params = new HashMap<>();
                    else
                        params = new HashMap<>(params);
                    StringBuilder newStr = new StringBuilder();
                    for (String key : cookies.keySet())
                        newStr.append(key).append("=").append(cookies.get(key)).append(";");
                    newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
                    params.put("Cookie", newStr.toString());
                }

                return params;
            }
        };
        requestQueue.add(request);
    }

    protected void post(String url, @Nullable final HashMap<String, String> cookies, @Nullable final HashMap<String, String> body, Response.Listener<Result> response, Response.ErrorListener error) {
        HttpRequest request = new HttpRequest(Request.Method.POST, url, response, error) {
            @Override
            public String getBodyContentType() {
                return "application/x-www-form-urlencoded";
            }

            @Override
            public byte[] getBody() {
                if (body==null) {
                    return null;
                }
                StringBuilder sb = new StringBuilder();
                for(HashMap.Entry<String, String> e: body.entrySet()){
                    if(sb.length() > 0){
                        sb.append('&');
                    }
                    try {
                        sb.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=').append(URLEncoder.encode(e.getValue(), "UTF-8"));
                    } catch (UnsupportedEncodingException ignored) {
                    }
                }
                String requestBody = sb.toString();
                try {
                    return requestBody.getBytes("utf-8");
                } catch (UnsupportedEncodingException e) {
                    return null;
                }
            }

            @Override
            public void deliverError(VolleyError error) {
                if (error == null) {
                    deliver(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
                } else if (error.networkResponse == null) {
                    int statusCode;
                    if (error instanceof TimeoutError)
                        statusCode = 408;
                    else
                        statusCode = 500;
                    deliver(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
                } else {
                    final int status = error.networkResponse.statusCode;
                    if (status == 302) {
                        deliverResponse(new Result(null, error.networkResponse.headers));
                    } else {
                        deliver(error);
                    }
                }
            }

            private void deliver(VolleyError error) {
                errorResponseListener.emit(error);
                super.deliverError(error);
            }

            @Override
            protected void deliverResponse(Result response) {
                successResponseListener.emit(response);
                super.deliverResponse(response);
            }

            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> params = super.getHeaders();
                if (cookies != null) {
                    if (params==null)
                        params = new HashMap<>();
                    else
                        params = new HashMap<>(params);
                    StringBuilder newStr = new StringBuilder();
                    for (String key : cookies.keySet())
                        newStr.append(key).append("=").append(cookies.get(key)).append(";");
                    newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
                    params.put("Cookie", newStr.toString());
                }

                return params;
            }
        };
        requestQueue.add(request);
    }

    protected Context getContext() {
        return mContext;
    }
}
