package org.sufficientlysecure.keychain.linked.resources;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;

import org.apache.http.client.methods.HttpGet;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.linked.LinkedTokenResource;
import org.sufficientlysecure.keychain.util.Log;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class GithubResource extends LinkedTokenResource {

    final String mHandle;
    final String mGistId;

    GithubResource(Set<String> flags, HashMap<String,String> params, URI uri,
            String handle, String gistId) {
        super(flags, params, uri);

        mHandle = handle;
        mGistId = gistId;
    }

    public static String generate(Context context, byte[] fingerprint) {
        String token = LinkedTokenResource.generate(fingerprint);

        return String.format(context.getResources().getString(R.string.linked_id_github_text), token);
    }

    @SuppressWarnings("deprecation") // HttpGet is deprecated
    @Override
    protected String fetchResource (Context context, OperationLog log, int indent)
            throws HttpStatusException, IOException, JSONException {

        log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString());
        indent += 1;

        HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId);
        String response = getResponseBody(context, httpGet);

        JSONObject obj = new JSONObject(response);

        JSONObject owner = obj.getJSONObject("owner");
        if (!mHandle.equals(owner.getString("login"))) {
            log.add(LogType.MSG_LV_ERROR_GITHUB_HANDLE, indent);
            return null;
        }

        JSONObject files = obj.getJSONObject("files");
        Iterator<String> it = files.keys();
        if (it.hasNext()) {
            // TODO can there be multiple candidates?
            JSONObject file = files.getJSONObject(it.next());
            return file.getString("content");
        }

        log.add(LogType.MSG_LV_ERROR_GITHUB_NOT_FOUND, indent);
        return null;

    }

    @Deprecated // not used for now, but could be used to pick up earlier posted gist if already present?
    @SuppressWarnings({ "deprecation", "unused" })
    public static GithubResource searchInGithubStream(
            Context context, String screenName, String needle, OperationLog log) {

        // narrow the needle down to important part
        Matcher matcher = magicPattern.matcher(needle);
        if (!matcher.find()) {
            throw new AssertionError("Needle must contain token pattern! This is a programming error, please report.");
        }
        needle = matcher.group();

        try {

            JSONArray array; {
                HttpGet httpGet =
                        new HttpGet("https://api.github.com/users/" + screenName + "/gists");
                httpGet.setHeader("Content-Type", "application/json");
                httpGet.setHeader("User-Agent", "OpenKeychain");

                String response = getResponseBody(context, httpGet);
                array = new JSONArray(response);
            }

            for (int i = 0, j = Math.min(array.length(), 5); i < j; i++) {
                JSONObject obj = array.getJSONObject(i);

                JSONObject files = obj.getJSONObject("files");
                Iterator<String> it = files.keys();
                if (it.hasNext()) {

                    JSONObject file = files.getJSONObject(it.next());
                    String type = file.getString("type");
                    if (!"text/plain".equals(type)) {
                        continue;
                    }
                    String id = obj.getString("id");
                    HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id);
                    httpGet.setHeader("User-Agent", "OpenKeychain");

                    JSONObject gistObj = new JSONObject(getResponseBody(context, httpGet));
                    JSONObject gistFiles = gistObj.getJSONObject("files");
                    Iterator<String> gistIt = gistFiles.keys();
                    if (!gistIt.hasNext()) {
                        continue;
                    }
                    // TODO can there be multiple candidates?
                    JSONObject gistFile = gistFiles.getJSONObject(gistIt.next());
                    String content = gistFile.getString("content");
                    if (!content.contains(needle)) {
                        continue;
                    }

                    URI uri = URI.create("https://gist.github.com/" + screenName + "/" + id);
                    return create(uri);
                }
            }

            // update the results with the body of the response
            log.add(LogType.MSG_LV_FETCH_ERROR_NOTHING, 2);
            return null;

        } catch (HttpStatusException e) {
            // log verbose output to logcat
            Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason());
            log.add(LogType.MSG_LV_FETCH_ERROR, 2, Integer.toString(e.getStatus()));
        } catch (MalformedURLException e) {
            log.add(LogType.MSG_LV_FETCH_ERROR_URL, 2);
        } catch (IOException e) {
            Log.e(Constants.TAG, "io error", e);
            log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2);
        } catch (JSONException e) {
            Log.e(Constants.TAG, "json error", e);
            log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 2);
        }

        return null;
    }

    public static GithubResource create(URI uri) {
        return create(new HashSet<String>(), new HashMap<String,String>(), uri);
    }

    public static GithubResource create(Set<String> flags, HashMap<String,String> params, URI uri) {

        // no params or flags
        if (!flags.isEmpty() || !params.isEmpty()) {
            return null;
        }

        Pattern p = Pattern.compile("https://gist\\.github\\.com/([a-zA-Z0-9_]+)/([0-9a-f]+)");
        Matcher match = p.matcher(uri.toString());
        if (!match.matches()) {
            return null;
        }
        String handle = match.group(1);
        String gistId = match.group(2);

        return new GithubResource(flags, params, uri, handle, gistId);

    }


    @Override
    public @DrawableRes
    int getDisplayIcon() {
        return R.drawable.linked_github;
    }

    @Override
    public @StringRes
    int getVerifiedText(boolean isSecret) {
        return isSecret ? R.string.linked_verified_secret_github : R.string.linked_verified_github;
    }

    @Override
    public String getDisplayTitle(Context context) {
        return context.getString(R.string.linked_title_github);
    }

    @Override
    public String getDisplayComment(Context context) {
        return mHandle;
    }

    @Override
    public boolean isViewable() {
        return true;
    }

    @Override
    public Intent getViewIntent() {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse(mSubUri.toString()));
        return intent;
    }
}
