package de.sebse.fuplanner.services.kvv.sync;

import android.content.Context;

import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import de.sebse.fuplanner.services.kvv.types.LoginTokenKVV;
import de.sebse.fuplanner.tools.network.HTTPService;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;

public class KVVLogin extends HTTPService {
    private final FULogin mFULogin;
    private long lastHash;
    private long lastSync;
    private static final long MAX_CACHE_TIME = 1000 * 60; // 1 minute

    public KVVLogin(Context context, FULogin fuLogin) {
        super(context);
        this.mFULogin = fuLogin;
    }


    public void testLoginToken(@NotNull LoginTokenKVV token, @NotNull NetworkCallback<LoginTokenKVV> callback, @NotNull NetworkErrorCallback errorCallback) {
        if (token.hashCode() == lastHash && lastSync + MAX_CACHE_TIME > System.currentTimeMillis() && token.getFullName() != null) {
            callback.onResponse(token);
            return;
        }
        get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", token.getUsername()), token.getCookies(), response -> {
            String body = response.getParsed();
            if (body == null) {
                errorCallback.onError(new NetworkError(100172, 403, "Testing login failed!"));
                return;
            }
            try {
                JSONObject json = new JSONObject(body);
                String displayName = json.getString("displayName");
                String email = json.getString("email");
                token.setAdditionals(displayName, email);
                lastSync = System.currentTimeMillis();
                lastHash = token.hashCode();
                callback.onResponse(token);
            } catch (JSONException e) {
                errorCallback.onError(new NetworkError(100171, 403, "Cannot parse profile!"));
            }
        }, error -> errorCallback.onError(new NetworkError(100170, error.networkResponse.statusCode, "Testing login failed!")));
    }















    public void doLogin(String username, String password, NetworkCallback<LoginTokenKVV> callback, NetworkErrorCallback error) {
        step1(success1 -> {
            String samlLocation = success1.get("Location");
            mFULogin.fulogin(samlLocation, username, password, samlResponse -> {
                step5(samlResponse, success5 -> {
                    String shibsessionKey = success5.get("shibsessionKey");
                    String shibsessionName = success5.get("shibsessionName");
                    step6(shibsessionKey, shibsessionName, success6 -> {
                        String kvvJSESSIONID = success6.get("JSESSIONID");
                        LoginTokenKVV token = new LoginTokenKVV(username, kvvJSESSIONID);
                        callback.onResponse(token);
                    }, error);
                }, error);
            }, error);
        }, error);
    }

    /*
     1= GET https://kvv.imp.fu-berlin.de/Shibboleth.sso/Login?entityID=https://identity.fu-berlin.de/idp-fub
        -> Location-Header: https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO?SAMLResponse=[SAMLResponse]&RelayState=[RelayState]
     */
    private void step1(final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
        get("https://kvv.imp.fu-berlin.de/Shibboleth.sso/Login?entityID=https://identity.fu-berlin.de/idp-fub", null, response -> {
            String location = response.getHeaders().get("Location");
            if (location==null) {
                errorCallback.onError(new NetworkError(100111, -1, "Error on getting SAML request!"));
                return;
            }
            HashMap<String, String> object = new HashMap<>();
            object.put("Location", location);
            callback.onResponse(object);
        }, error -> errorCallback.onError(new NetworkError(100110, error.networkResponse.statusCode, "Error on getting SAML request!")));
    }

    /*
     5= POST https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST
        + Body: SAMLResponse=[SAML-RESPONSE]
        + Header: Content-Type: application/x-www-form-urlencoded
        -> Set-Cookie: _shibsession_[SESS-NR]: [SESS-VALUE]
    */
    private void step5(String SAMLResponse, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
        HashMap<String, String> body = new HashMap<>();
        body.put("SAMLResponse", SAMLResponse);
        post("https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST", null, body, response -> {
            String cookies = response.getHeaders().get("Set-Cookie");
            if (cookies ==null) {
                errorCallback.onError(new NetworkError(100151, -1, "Error on starting KVV session!"));
                return;
            }
            HashMap<String, String> object = new HashMap<>();


            Pattern pattern = Pattern.compile("(_shibsession_[0-9a-f]+)=([^;]+);");
            Matcher matcher = pattern.matcher(cookies);
            if (!matcher.find()) {
                errorCallback.onError(new NetworkError(100152, -1, "Error on starting KVV session!"));
            }
            object.put("shibsessionKey", matcher.group(1));
            object.put("shibsessionName", matcher.group(2));

            callback.onResponse(object);
        }, error -> errorCallback.onError(new NetworkError(100150, error.networkResponse.statusCode, "Error on starting KVV session!")));
    }


    /*
     6= https://kvv.imp.fu-berlin.de/sakai-login-tool/container
        + Cookie: _shibsession_[SESS-NR]: [SESS-VALUE]
        -> Set-Cookie: JSESSIONID: [JSESSION-KVV]
	*/
    private void step6(String shibsessionKey, String shibsessionName, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
        HashMap<String, String> cookies = new HashMap<>();
        cookies.put(shibsessionKey, shibsessionName);
        get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", cookies, response -> {
            String cookies1 = response.getHeaders().get("Set-Cookie");
            if (cookies1 ==null) {
                errorCallback.onError(new NetworkError(100161, -1, "Cannot finish login process!"));
                return;
            }
            HashMap<String, String> object;
            try {
                object = getCookie(cookies1, new String[]{"JSESSIONID"});
            } catch (NoSuchFieldException e) {
                errorCallback.onError(new NetworkError(100162, -1, "Cannot finish login process!"));
                return;
            }
            callback.onResponse(object);
        }, error -> errorCallback.onError(new NetworkError(100160, error.networkResponse.statusCode, "Cannot finish login process!")));
    }
}
