package com.fox2code.mmm.repo;

import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.utils.io.Hashes;
import com.fox2code.mmm.utils.io.PropUtils;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.realm.ReposList;

import org.json.JSONObject;

import java.nio.charset.StandardCharsets;

import io.realm.Realm;
import io.realm.RealmConfiguration;
import timber.log.Timber;

public class CustomRepoManager {
    public static final int MAX_CUSTOM_REPOS = 5;
    private static final boolean AUTO_RECOMPILE = true;
    private final RepoManager repoManager;
    private final String[] customRepos;
    boolean dirty;
    private int customReposCount;

    @SuppressWarnings("unused")
    CustomRepoManager(MainApplication mainApplication, RepoManager repoManager) {
        this.repoManager = repoManager;
        this.customRepos = new String[MAX_CUSTOM_REPOS];
        this.customReposCount = 0;
        // refuse to load if setup is not complete
        if (MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", "").equals("")) {
            return;
        }
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build();
        Realm realm = Realm.getInstance(realmConfiguration);
        if (realm.isInTransaction()) {
            realm.commitTransaction();
        }
        int i = 0;
        @SuppressWarnings("MismatchedReadAndWriteOfArray") final int[] lastFilled = {0};
        realm.executeTransaction(realm1 -> {
            // find all repos that are not built-in
            for (ReposList reposList : realm1.where(ReposList.class).notEqualTo("id", "androidacy_repo").and().notEqualTo("id", "magisk_alt_repo").and().notEqualTo("id", "magisk_official_repo").findAll()) {
                String repo = reposList.getUrl();
                if (!PropUtils.isNullString(repo) && !RepoManager.isBuiltInRepo(repo)) {
                    lastFilled[0] = i;
                    int index = AUTO_RECOMPILE ? this.customReposCount : i;
                    this.customRepos[index] = repo;
                    this.customReposCount++;
                    ((CustomRepoData) this.repoManager.addOrGet(repo)).override = "custom_repo_" + index;
                }
            }
        });
    }

    public CustomRepoData addRepo(String repo) {
        if (RepoManager.isBuiltInRepo(repo))
            throw new IllegalArgumentException("Can't add built-in repo to custom repos");
        for (String repoEntry : this.customRepos) {
            if (repo.equals(repoEntry)) return (CustomRepoData) this.repoManager.get(repoEntry);
        }
        int i = 0;
        while (customRepos[i] != null) i++;
        customRepos[i] = repo;
        // fetch that sweet sweet json
        byte[] json;
        try {
            json = Http.doHttpGet(repo, false);
        } catch (Exception e) {
            Timber.e(e, "Failed to fetch json from repo");
            return null;
        }
        // get website, support, donate, submitModule. all optional. name is required.
        // parse json
        JSONObject jsonObject;
        try {
            jsonObject = new JSONObject(new String(json));
        } catch (Exception e) {
            Timber.e(e, "Failed to parse json from repo");
            return null;
        }
        // get name
        String name;
        try {
            name = jsonObject.getString("name");
        } catch (Exception e) {
            Timber.e(e, "Failed to get name from json");
            return null;
        }
        // get website
        String website;
        try {
            website = jsonObject.getString("website");
        } catch (Exception e) {
            website = null;
        }
        // get support
        String support;
        try {
            support = jsonObject.getString("support");
        } catch (Exception e) {
            support = null;
        }
        // get donate
        String donate;
        try {
            donate = jsonObject.getString("donate");
        } catch (Exception e) {
            donate = null;
        }
        // get submitModule
        String submitModule;
        try {
            submitModule = jsonObject.getString("submitModule");
        } catch (Exception e) {
            submitModule = null;
        }
        String id = "repo_" + Hashes.hashSha256(repo.getBytes(StandardCharsets.UTF_8));
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getKey()).allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build();
        Realm realm = Realm.getInstance(realmConfiguration);
        String finalWebsite = website;
        String finalSupport = support;
        String finalDonate = donate;
        String finalSubmitModule = submitModule;
        realm.executeTransaction(realm1 -> {
            // find the matching entry for repo_0, repo_1, etc.
            ReposList reposList = realm1.where(ReposList.class).equalTo("id", id).findFirst();
            if (reposList == null) {
                reposList = realm1.createObject(ReposList.class, id);
            }
            reposList.setUrl(repo);
            reposList.setName(name);
            reposList.setWebsite(finalWebsite);
            reposList.setSupport(finalSupport);
            reposList.setDonate(finalDonate);
            reposList.setSubmitModule(finalSubmitModule);
            reposList.setEnabled(true);
            // save the object
            realm1.copyToRealmOrUpdate(reposList);
        });
        customReposCount++;
        this.dirty = true;
        CustomRepoData customRepoData = (CustomRepoData) this.repoManager.addOrGet(repo);
        customRepoData.override = "repo_" + id;
        customRepoData.id = id;
        customRepoData.website = website;
        customRepoData.support = support;
        customRepoData.donate = donate;
        customRepoData.submitModule = submitModule;
        customRepoData.name = name;
        // Set the enabled state to true
        customRepoData.setEnabled(true);
        customRepoData.updateEnabledState();
        realm.close();
        return customRepoData;
    }

    public CustomRepoData getRepo(String id) {
        return (CustomRepoData) this.repoManager.get(id);
    }

    public void removeRepo(int index) {
        String oldRepo = customRepos[index];
        if (oldRepo != null) {
            customRepos[index] = null;
            customReposCount--;
            CustomRepoData customRepoData = (CustomRepoData) this.repoManager.get(oldRepo);
            if (customRepoData != null) {
                customRepoData.setEnabled(false);
                customRepoData.override = null;
            }
            this.dirty = true;
        }
    }

    public boolean hasRepo(String repo) {
        for (String repoEntry : this.customRepos) {
            if (repo.equals(repoEntry)) return true;
        }
        return false;
    }

    public boolean canAddRepo() {
        return this.customReposCount < MAX_CUSTOM_REPOS;
    }

    public boolean canAddRepo(String repo) {
        if (RepoManager.isBuiltInRepo(repo) || this.hasRepo(repo) || !this.canAddRepo())
            return false;
        return repo.startsWith("https://") && repo.indexOf('/', 9) != -1;
    }

    public int getRepoCount() {
        return this.customReposCount;
    }

    public boolean needUpdate() {
        boolean needUpdate = this.dirty;
        if (needUpdate) this.dirty = false;
        return needUpdate;
    }
}
