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.LoginTokenBB;
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 BBLogin extends HTTPService {
    private final FULogin mFULogin;
    private long lastHash;
    private long lastSync;
    private static final long MAX_CACHE_TIME = 1000 * 60; // 1 minute

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

    public void testLoginToken(@NotNull LoginTokenBB token, @NotNull NetworkCallback<LoginTokenBB> callback, @NotNull NetworkErrorCallback errorCallback) {
        if (token.hashCode() == lastHash && lastSync + MAX_CACHE_TIME > System.currentTimeMillis() && token.getStudentId() != null) {
            callback.onResponse(token);
            return;
        }
        get(String.format("https://lms.fu-berlin.de/learn/api/public/v1/users/?userName=%s", token.getUsername()), token.getCookies(), response -> {
            String body = response.getParsed();
            if (body == null) {
                errorCallback.onError(new NetworkError(100272, 403, "Testing login failed!"));
                return;
            }
            try {
                JSONObject json = new JSONObject(body);
                json = json.getJSONArray("results").getJSONObject(0);
                String id = json.getString("id");
                String studentId = json.getString("studentId");
                token.setAdditionals(id, studentId);
                lastSync = System.currentTimeMillis();
                lastHash = token.hashCode();
                callback.onResponse(token);
            } catch (JSONException e) {
                errorCallback.onError(new NetworkError(100271, 403, "Cannot parse profile!"));
            }
        }, error -> errorCallback.onError(new NetworkError(100270, error.networkResponse.statusCode, "Testing login failed!")));
    }















    public void doLogin(String username, String password, NetworkCallback<LoginTokenBB> 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 s_session_id = success6.get("s_session_id");
                        String session_id = success6.get("session_id");
                        LoginTokenBB token = new LoginTokenBB(username, s_session_id, session_id);
                        callback.onResponse(token);
                    }, error);
                }, error);
            }, error);
        }, error);
    }

    /*
     1= GET https://lms.fu-berlin.de/lms-apps/login/sso/index.php
        -> 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://lms.fu-berlin.de/lms-apps/login/sso/index.php", null, response -> {
            String location = response.getHeaders().get("Location");
            if (location==null) {
                errorCallback.onError(new NetworkError(100211, -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(100210, error.networkResponse.statusCode, "Error on getting SAML request!")));
    }



    /*
     5= POST https://lms.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://lms.fu-berlin.de/Shibboleth.sso/SAML2/POST", null, body, response -> {
            String cookies = response.getHeaders().get("Set-Cookie");
            if (cookies ==null) {
                errorCallback.onError(new NetworkError(100251, -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(100252, -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(100250, error.networkResponse.statusCode, "Error on starting KVV session!")));
    }


    /*
     6= https://lms.fu-berlin.de/webapps/bb-auth-provider-shibboleth-bb_bb60/execute/shibbolethLogin?returnUrl=https://lms.fu-berlin.de/webapps/portal/execute/defaultTab&authProviderId=_3_1
        + 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://lms.fu-berlin.de/webapps/bb-auth-provider-shibboleth-bb_bb60/execute/shibbolethLogin?returnUrl=https://lms.fu-berlin.de/webapps/portal/execute/defaultTab&authProviderId=_3_1", cookies, response -> {
            String cookies1 = response.getHeaders().get("Set-Cookie");
            if (cookies1 ==null) {
                errorCallback.onError(new NetworkError(100261, -1, "Cannot finish login process!"));
                return;
            }
            HashMap<String, String> object;
            try {
                object = getCookie(cookies1, new String[]{"session_id", "s_session_id"});
            } catch (NoSuchFieldException e) {
                errorCallback.onError(new NetworkError(100262, -1, "Cannot finish login process!"));
                return;
            }
            callback.onResponse(object);
        }, error -> errorCallback.onError(new NetworkError(100260, error.networkResponse.statusCode, "Cannot finish login process!")));
    }
}
