package io.lbry.browser.tasks;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.AsyncTask;
import android.util.Log;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.lbry.browser.MainActivity;
import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.exceptions.LbryioRequestException;
import io.lbry.browser.exceptions.LbryioResponseException;
import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri;
import io.lbry.browser.utils.Lbryio;
import okhttp3.Response;

// background task to create a diff of local and remote subscriptions and try to merge
public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscription>> {
    private static final String TAG = "MergeSubscriptionsTask";
    private Context context;
    private List<Subscription> base;
    private List<Subscription> diff;
    private MergeSubscriptionsHandler handler;
    private Exception error;
    private boolean replaceLocal;

    public MergeSubscriptionsTask(List<Subscription> base, boolean replaceLocal, Context context, MergeSubscriptionsHandler handler) {
        this.base = base;
        this.replaceLocal = replaceLocal;
        this.context = context;
        this.handler = handler;
    }

    protected List<Subscription> doInBackground(Void... params) {
        List<Subscription> combined = new ArrayList<>(base);
        List<Subscription> localSubs = new ArrayList<>();
        List<Subscription> remoteSubs = new ArrayList<>();
        diff = new ArrayList<>();
        SQLiteDatabase db = null;
        try {
            // fetch local subscriptions
            if (context instanceof MainActivity) {
                db = ((MainActivity) context).getDbHelper().getWritableDatabase();
            }
            if (db != null) {
                if (replaceLocal) {
                    DatabaseHelper.clearSubscriptions(db);
                    for (Subscription sub : base) {
                        DatabaseHelper.createOrUpdateSubscription(sub, db);
                    }
                } else {
                    localSubs = DatabaseHelper.getSubscriptions(db);
                    for (Subscription sub : localSubs) {
                        if (!combined.contains(sub)) {
                            combined.add(sub);
                        }
                    }
                }
            }

            // fetch remote subscriptions
            JSONArray array = (JSONArray) Lbryio.parseResponse(Lbryio.call("subscription", "list", context));
            if (array != null) {
                for (int i = 0; i < array.length(); i++) {
                    JSONObject item = array.getJSONObject(i);
                    String claimId = item.getString("claim_id");
                    String channelName = item.getString("channel_name");

                    LbryUri url = new LbryUri();
                    url.setChannelName(channelName);
                    url.setClaimId(claimId);
                    Subscription subscription = new Subscription(channelName, url.toString());
                    remoteSubs.add(subscription);
                }
            }

            for (int i = 0; i < combined.size(); i++) {
                Subscription local = combined.get(i);
                if (!remoteSubs.contains(local)) {
                    // add to remote subscriptions
                    try {
                        LbryUri uri = LbryUri.parse(local.getUrl());
                        Map<String, String> options = new HashMap<>();
                        String channelClaimId = uri.getChannelClaimId();
                        String channelName = Helper.normalizeChannelName(local.getChannelName());
                        if (!Helper.isNullOrEmpty(channelClaimId) && !Helper.isNullOrEmpty(channelName)) {
                            options.put("claim_id", channelClaimId);
                            options.put("channel_name", channelName);
                            Lbryio.parseResponse(Lbryio.call("subscription", "new", options, context));
                        }
                    } catch (LbryUriException | LbryioRequestException | LbryioResponseException ex) {
                        // pass
                        Log.e(TAG, String.format("subscription/new failed: %s", ex.getMessage()), ex);
                    }
                }
            }

            if (!replaceLocal) {
                for (int i = 0; i < localSubs.size(); i++) {
                    Subscription local = localSubs.get(i);
                    if (!base.contains(local) && !diff.contains(local)) {
                        diff.add(local);
                    }
                }
            }
            for (int i = 0; i < remoteSubs.size(); i++) {
                Subscription remote = remoteSubs.get(i);
                if (!combined.contains(remote)) {
                    combined.add(remote);
                    if (!diff.contains(remote)) {
                        diff.add(remote);
                    }
                }
            }
        } catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException | IllegalStateException | SQLiteException ex) {
            error = ex;
            return null;
        }

        return combined;
    }
    protected void onPostExecute(List<Subscription> subscriptions) {
        if (handler != null) {
            if (subscriptions != null) {
                handler.onSuccess(subscriptions, diff);
            } else {
                handler.onError(error);
            }
        }
    }

    public interface MergeSubscriptionsHandler {
        void onSuccess(List<Subscription> subscriptions, List<Subscription> diff);
        void onError(Exception error);
    }
}