package de.sebse.fuplanner.services.kvv;

import android.content.Context;
import android.text.TextUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.regex.MatchResult;

import de.sebse.fuplanner.services.kvv.types.Event;
import de.sebse.fuplanner.services.kvv.types.EventList;
import de.sebse.fuplanner.services.kvv.types.Modules;
import de.sebse.fuplanner.tools.Regex;
import de.sebse.fuplanner.tools.UtilsDate;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;

public class ModulesEvents extends PartModules<EventList> {

    ModulesEvents(Login login, ModulesList list, Context context) {
        super(login, list, context);
    }

    @Override
    protected EventList getPart(Modules.Module module) {
        return module.events;
    }

    @Override
    protected boolean setPart(Modules.Module module, EventList part) {
        boolean changed = module.events == null || module.events.hashCode() != part.hashCode();
        module.events = part;
        return changed;
    }

    @Override
    protected void upgradeKVV(final String ID, final NetworkCallback<EventList> callback, final NetworkErrorCallback errorCallback) {
        if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenKVV() == null || !mLogin.getLoginTokenKVV().isAvailable()) {
            errorCallback.onError(new NetworkError(101404, 500, "Currently running in offline mode!"));
            return;
        }
        get(String.format("https://kvv.imp.fu-berlin.de/direct/calendar/site/%s.json?detailed=true", ID), mLogin.getLoginTokenKVV().getCookies(), response -> {
            String body = response.getParsed();
            if (body == null) {
                errorCallback.onError(new NetworkError(101401, 403, "No events retrieved!"));
                return;
            }
            EventList events = new EventList();
            JSONArray sites;
            try {
                JSONObject json = new JSONObject(body);
                sites = json.getJSONArray("calendar_collection");
            } catch (JSONException e) {
                e.printStackTrace();
                errorCallback.onError(new NetworkError(101402, 403, "Cannot parse events!"));
                return;
            }

            for (int i = 0; i < sites.length(); i++) {
                try {
                    JSONObject site = sites.getJSONObject(i);
                    String id = site.getString("eventId");
                    String type = site.getString("type");
                    String title = site.getString("title");
                    String siteId = site.getString("siteId");
                    long duration = site.getLong("duration");
                    long firstTime = site.getJSONObject("firstTime").getLong("time");
                    String location = site.getString("location");
                    events.add(new Event(id, type, title, duration, firstTime, siteId, location));
                } catch (JSONException e) {
                    log.e(new NetworkError(101405, 403, "Cannot parse events!"));
                    e.printStackTrace();
                    log.e("ID:", i, "JSON:", sites);
                    return;
                }
            }

            // Empty events *may be* because token is invalid -> check
            if (events.size() == 0)
                mLogin.testLoginToken(token -> callback.onResponse(events), errorCallback);
            else
                callback.onResponse(events);
        }, error -> errorCallback.onError(new NetworkError(101403, error.networkResponse.statusCode, "Cannot get events!")));
    }

    @Override
    protected void upgradeBB(String ID, NetworkCallback<EventList> callback, NetworkErrorCallback errorCallback) {
        if (!mLogin.isInOnlineMode() || mLogin.getLoginTokenBB() == null || !mLogin.getLoginTokenBB().isAvailable()) {
            errorCallback.onError(new NetworkError(101414, 500, "Currently running in offline mode!"));
            return;
        }
        this.mList.recv(modules -> {
            Modules.Module module = modules.get(ID);
            if (module == null) {
                errorCallback.onError(new NetworkError(101416, 400, "Cannot get events!"));
                return;
            }
            if (module.lvNumber.size() == 0) {
                // Events not available
                callback.onResponse(new EventList());
                return;
            }
            super.head(String.format("https://www.fu-berlin.de/vv/de/search?utf8=✓&query=%s", module.lvNumber.iterator().next()), null, response -> {
                String location = response.getHeaders().get("Location");
                if (location == null) {
                    // Events not available
                    callback.onResponse(new EventList());
                    //errorCallback.onError(new NetworkError(101410, 403, "Cannot get events!"));
                    return;
                }
                String group;
                try {
                    group = Regex.regex("lv/([0-9]+)\\?", location);
                } catch (NoSuchFieldException e) {
                    errorCallback.onError(new NetworkError(101410, 400, "Cannot get events!"));
                    e.printStackTrace();
                    return;
                }
                // 462854, 465661, 462126, 463782, 437050, 433843, 471614, 464205
                //String[] tests = {"462854", "465661", "462126", "463782", "437050", "433843", "471614", "464205"};
                //String[] tests = {"461459", "424564", "459494", "429737", "463765", "476477", "464082", "459577", "459743", "464318", "449358", "454327", "461784", "468081", "485919"};
                //group = tests[new Random().nextInt(tests.length)];
                //log.d("LAAAAAAST", group);
                //group = "462126";
                super.get(String.format("https://www.fu-berlin.de/vv/de/lv/%s", group), null, response1 -> {
                    String body = response1.getParsed();
                    if (body == null) {
                        errorCallback.onError(new NetworkError(101411, 400, "Cannot get events!"));
                        return;
                    }
                    EventList events = new EventList();
                    for (MatchResult match : Regex.allMatches("<span id=\"link_to_details_[^~]*?</span>", body)) {
                        String entry = match.group(0);
                        String date;
                        String title = "";
                        try {
                            date = Regex.regex("[A-Z][a-z], [0-9]{2}\\.[0-9]{2}\\.[0-9]{4} [0-9]{2}:[0-9]{2} - [0-9]{2}:[0-9]{2}", entry, 0);
                        } catch (NoSuchFieldException e) {
                            errorCallback.onError(new NetworkError(101412, 400, "Cannot get events!"));
                            e.printStackTrace();
                            return;
                        }
                        try {
                            title = Regex.regex("<div class=\"course_title\">([^<]*?)</div>", entry, 1);
                        } catch (NoSuchFieldException ignored) {}
                        if (TextUtils.isEmpty(title))
                            title = module.title;
                        ArrayList<String> docents = new ArrayList<>();
                        ArrayList<String> locations = new ArrayList<>();
                        for (MatchResult match1 : Regex.allMatches("<div class=\"appointment_details_column\">[^~]*?</div>", entry)) {
                            if (match1.group(0).contains("Dozenten:")) {
                                for (MatchResult match2 : Regex.allMatches(">([^>]*?)<small class=\"phone_portal\">", match1.group(0))) {
                                    docents.add(match2.group(0).trim());
                                }
                            } else if (match1.group(0).contains("Räume:")) {
                                for (MatchResult match2 : Regex.allMatches("</b>([^~]*?)</p>", match1.group(0))) {
                                    locations.add(match2.group(1).trim());
                                }
                            }
                        }
                        long start = UtilsDate.stringToMillis(date.substring(4, 20), "dd.MM.yyyy HH:mm");
                        long duration = (Integer.parseInt(date.substring(23, 25))-Integer.parseInt(date.substring(15, 17)))*60
                                + Integer.parseInt(date.substring(26, 28))-Integer.parseInt(date.substring(18, 20));
                        duration = duration*60*1000-1;
                        String locationString = TextUtils.join(", ", locations);
                        events.add(new Event("", "Class section - Lecture", title, duration, start, ID, locationString));
                    }
                    callback.onResponse(events);
                }, error -> errorCallback.onError(new NetworkError(101415, error.networkResponse.statusCode, "Cannot get events!")));
            }, error -> errorCallback.onError(new NetworkError(101413, error.networkResponse.statusCode, "Cannot get events!")));
        }, errorCallback);
    }
}
