package ctbrec.ui.sites.camsoda;

import static ctbrec.Model.State.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ctbrec.Model;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import ctbrec.sites.camsoda.Camsoda;
import ctbrec.sites.camsoda.CamsodaModel;
import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.tabs.PaginatedScheduledService;
import javafx.concurrent.Task;
import okhttp3.Request;
import okhttp3.Response;

public class CamsodaUpdateService extends PaginatedScheduledService {

    private static final Logger LOG = LoggerFactory.getLogger(CamsodaUpdateService.class);

    private String url;
    private boolean loginRequired;
    private Camsoda camsoda;
    int modelsPerPage = 50;

    private Predicate<CamsodaModel> filter;

    public CamsodaUpdateService(String url, boolean loginRequired, Camsoda camsoda, Predicate<CamsodaModel> filter) {
        this.url = url;
        this.loginRequired = loginRequired;
        this.camsoda = camsoda;
        this.filter = filter;
    }

    @Override
    protected Task<List<Model>> createTask() {
        return new Task<List<Model>>() {
            @Override
            public List<Model> call() throws IOException {
                return loadOnlineModels().stream()
                        .sorted((m1,m2) -> (int)(m2.getSortOrder() - m1.getSortOrder()))
                        .filter(filter)
                        .skip( (page-1) * modelsPerPage)
                        .limit(modelsPerPage)
                        .collect(Collectors.toList());
            }
        };
    }

    protected List<CamsodaModel> loadOnlineModels() throws IOException {
        List<CamsodaModel> models = new ArrayList<>();
        if(loginRequired && StringUtil.isBlank(ctbrec.Config.getInstance().getSettings().camsodaUsername)) {
            return Collections.emptyList();
        } else {
            LOG.debug("Fetching page {}", url);
            if(loginRequired) {
                SiteUiFactory.getUi(camsoda).login();
            }
            Request request = new Request.Builder().url(url).build();
            try (Response response = camsoda.getHttpClient().execute(request)) {
                if (response.isSuccessful()) {
                    String body = response.body().string();
                    JSONObject json = new JSONObject(body);
                    JSONArray template = json.getJSONArray("template");
                    JSONArray results = json.getJSONArray("results");
                    for (int i = 0; i < results.length(); i++) {
                        JSONObject result = results.getJSONObject(i);
                        Object templateObject = result.get("tpl");
                        CamsodaModel model;
                        try {
                            if (templateObject instanceof JSONObject) {
                                JSONObject tpl = (JSONObject) templateObject;
                                String name = tpl.getString(Integer.toString(getTemplateIndex(template, "username")));
                                model = (CamsodaModel) camsoda.createModel(name);
                                model.setDescription(tpl.getString(Integer.toString(getTemplateIndex(template, "subject_html"))));
                                model.setSortOrder(tpl.getFloat(Integer.toString(getTemplateIndex(template, "sort_value"))));
                                model.setNew(result.optBoolean("new"));
                                model.setGender(tpl.getString(Integer.toString(getTemplateIndex(template, "gender"))));
                                String preview = "https:" + tpl.getString(Integer.toString(getTemplateIndex(template, "thumb")));
                                model.setPreview(preview);
                                String displayName = tpl.getString(Integer.toString(getTemplateIndex(template, "display_name")));
                                model.setDisplayName(displayName.replaceAll("[^a-zA-Z0-9]", ""));
                                if (model.getDisplayName().isBlank()) {
                                    model.setDisplayName(name);
                                }
                                model.setOnlineState(tpl.getString(Integer.toString(getTemplateIndex(template, "stream_name"))).isEmpty() ? OFFLINE : ONLINE);
                                try {
                                    String statusIndex = Integer.toString(getTemplateIndex(template, "status"));
                                    if (tpl.has(statusIndex)) {
                                        model.setOnlineStateByStatus(tpl.getString(statusIndex));
                                    }
                                } catch (NoSuchElementException e) {
                                }
                                models.add(model);
                            } else if (templateObject instanceof JSONArray) {
                                JSONArray tpl = (JSONArray) templateObject;
                                String name = tpl.getString(getTemplateIndex(template, "username"));
                                model = (CamsodaModel) camsoda.createModel(name);
                                model.setSortOrder(tpl.getFloat(getTemplateIndex(template, "sort_value")));
                                model.setDescription(tpl.getString(getTemplateIndex(template, "subject_html")));
                                String preview = "https:" + tpl.getString(getTemplateIndex(template, "thumb"));
                                model.setPreview(preview);
                                model.setOnlineStateByStatus(tpl.getString(getTemplateIndex(template, "status")));
                                String displayName = tpl.getString(getTemplateIndex(template, "display_name"));
                                model.setDisplayName(displayName.replaceAll("[^a-zA-Z0-9]", ""));
                                if (model.getDisplayName().isBlank()) {
                                    model.setDisplayName(name);
                                }
                                models.add(model);
                            }
                        } catch (Exception e) {
                            LOG.warn("Couldn't parse one of the models: {}", result, e);
                        }
                    }
                    return models;
                } else {
                    throw new HttpException(response.code(), response.message());
                }
            }
        }
    }

    private int getTemplateIndex(JSONArray template, String string) {
        for (int i = 0; i < template.length(); i++) {
            String s = template.getString(i);
            if(Objects.equals(s, string)) {
                return i;
            }
        }
        throw new NoSuchElementException(string + " not found in template: " + template.toString());
    }

    public void setFilter(Predicate<CamsodaModel> filter) {
        this.filter = filter;
    }
}
