package org.wikipedia.descriptions;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;

import org.json.JSONArray;
import org.wikipedia.WikipediaApp;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.mwapi.MwException;
import org.wikipedia.dataclient.mwapi.MwServiceError;
import org.wikipedia.dataclient.retrofit.MwCachedService;
import org.wikipedia.dataclient.retrofit.RetrofitException;
import org.wikipedia.dataclient.retrofit.WikiCachedService;
import org.wikipedia.login.User;
import org.wikipedia.page.Page;
import org.wikipedia.page.PageProperties;
import org.wikipedia.page.PageTitle;
import org.wikipedia.util.ReleaseUtil;

import java.util.Arrays;

import retrofit2.Call;
import retrofit2.Response;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;

/**
 * Data Client to submit a new or updated description to wikidata.org.
 */
public class DescriptionEditClient {
    private static final String ABUSEFILTER_DISALLOWED = "abusefilter-disallowed";
    private static final String ABUSEFILTER_WARNING = "abusefilter-warning";

    public interface Callback {
        void success(@NonNull Call<DescriptionEdit> call);
        void abusefilter(@NonNull Call<DescriptionEdit> call, @Nullable String code, @Nullable String info);
        void invalidLogin(@NonNull Call<DescriptionEdit> call, @NonNull Throwable caught);
        void failure(@NonNull Call<DescriptionEdit> call, @NonNull Throwable caught);
    }

    public static boolean isEditAllowed(@NonNull Page page) {
        PageProperties props = page.getPageProperties();
        return !TextUtils.isEmpty(props.getWikiBaseItem())
                && (!isLanguageBlacklisted(page.getTitle().getWikiSite().languageCode())
                || ReleaseUtil.isPreBetaRelease());
    }

    private static boolean isLanguageBlacklisted(@NonNull String lang) {
        JSONArray blacklist = WikipediaApp.getInstance().getRemoteConfig().getConfig()
                .optJSONArray("descriptionEditLangBlacklist");
        if (blacklist != null) {
            for (int i = 0; i < blacklist.length(); i++) {
                if (lang.equals(blacklist.optString(i))) {
                    return true;
                }
            }
            return false;
        } else {
            return Arrays.asList("en", "de", "it", "fr", "es", "ja", "nl", "pt", "tr", "zh-hant")
                    .contains(lang);
        }
    }

    @NonNull private final WikiCachedService<Service> cachedService
            = new MwCachedService<>(Service.class);

    /**
     * Submit a new value for the Wikidata description associated with the given Wikipedia page.
     *
     * @param wiki             the Wiki site to use this on. Should be "www.wikidata.org"
     * @param pageTitle        specifies the Wikipedia page the Wikidata item is linked to
     * @param description      the new value for the Wikidata description
     * @param editToken        a token from Wikidata
     * @param cb               called when this is done successfully or failed
     * @return Call object which can be used to cancel the request
     */
    public Call<DescriptionEdit> request(@NonNull WikiSite wiki,
                                         @NonNull PageTitle pageTitle,
                                         @NonNull String description,
                                         @NonNull String editToken,
                                         @NonNull Callback cb) {
        return request(cachedService.service(wiki), pageTitle, description, editToken,
                User.isLoggedIn(), cb);
    }

    @SuppressWarnings("WeakerAccess") @VisibleForTesting
    Call<DescriptionEdit> request(@NonNull Service service,
                                  @NonNull PageTitle pageTitle,
                                  @NonNull String description,
                                  @NonNull String editToken,
                                  boolean loggedIn,
                                  @NonNull final Callback cb) {

        Call<DescriptionEdit> call = service.edit(pageTitle.getWikiSite().languageCode(),
                pageTitle.getWikiSite().languageCode(), pageTitle.getWikiSite().dbName(),
                pageTitle.getPrefixedText(), description, editToken,
                loggedIn ? "user" : null);
        call.enqueue(new retrofit2.Callback<DescriptionEdit>() {
            @Override
            public void onResponse(Call<DescriptionEdit> call,
                                   Response<DescriptionEdit> response) {
                final DescriptionEdit body = response.body();
                if (body.editWasSuccessful()) {
                    cb.success(call);
                } else if (body.hasError()) {
                    handleError(call, body, cb);
                } else {
                    cb.failure(call,
                            RetrofitException.unexpectedError(new RuntimeException(
                                    "Received unrecognized description edit response")));
                }
            }

            @Override
            public void onFailure(Call<DescriptionEdit> call, Throwable t) {
                cb.failure(call, t);
            }
        });
        return call;
    }

    private void handleError(@NonNull Call<DescriptionEdit> call, @NonNull DescriptionEdit body,
                             @NonNull Callback cb) {
        MwServiceError error = body.getError();
        String info = body.info();
        RuntimeException exception = new RuntimeException(info != null
                ? info : "An unknown error occurred");

        if (body.badLoginState() || body.badToken()) {
            cb.invalidLogin(call, exception);
        } else if (error != null && error.hasMessageName(ABUSEFILTER_DISALLOWED)) {
            cb.abusefilter(call, ABUSEFILTER_DISALLOWED, error.getMessageHtml(ABUSEFILTER_DISALLOWED));
        } else if (error != null && error.hasMessageName(ABUSEFILTER_WARNING)) {
            cb.abusefilter(call, ABUSEFILTER_WARNING, error.getMessageHtml(ABUSEFILTER_WARNING));
        } else {
            // noinspection ConstantConditions
            cb.failure(call, new MwException(error));
        }
    }

    @VisibleForTesting interface Service {
        @POST("w/api.php?action=wbsetdescription&format=json") @FormUrlEncoded
        Call<DescriptionEdit> edit(@NonNull @Field("language") String language,
                                   @NonNull @Field("uselang") String useLang,
                                   @NonNull @Field("site") String site,
                                   @NonNull @Field("title") String title,
                                   @NonNull @Field("value") String newDescription,
                                   @NonNull @Field("token") String token,
                                   @Nullable @Field("assert") String user);
    }
}

@@ -6439,16 +6439,32 @@
 mat=json
+&formatversion=2
 %22) @Form

Update DescriptionEditClient to use formatversion=2