/*
 * Copyright 2015 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.apiman.gateway.engine.es;

import io.searchbox.action.Action;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.cluster.Health;
import io.searchbox.indices.CreateIndex;
import io.searchbox.indices.IndicesExists;

import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;

/**
 * Base class for client factories.  Provides caching of clients.
 *
 * @author eric.wittmann@redhat.com
 */
public abstract class AbstractClientFactory {

    protected static Map<String, JestClient> clients = new HashMap<>();

    /**
     * Clears all the clients from the cache.  Useful for unit testing.
     */
    public static void clearClientCache() {
        clients.clear();
    }

    /**
     * Constructor.
     */
    public AbstractClientFactory() {
    }

    /**
     * Called to initialize the storage.
     * @param client the jest client
     * @param indexName the name of the ES index to initialize
     * @param defaultIndexName the default ES index - used to determine which -settings.json file to use
     */
    protected void initializeClient(JestClient client, String indexName, String defaultIndexName) {
        try {
            client.execute(new Health.Builder().build());
            Action<JestResult> action = new IndicesExists.Builder(indexName).build();
            // There was occasions where a race occurred here when multiple threads try to
            // create the index simultaneously. This caused a non-fatal, but annoying, exception.
            synchronized(AbstractClientFactory.class) {
                JestResult result = client.execute(action);
                if (!result.isSucceeded()) {
                    createIndex(client, indexName, defaultIndexName + "-settings.json"); //$NON-NLS-1$
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Creates an index.
     * @param indexName
     * @throws Exception
     */
    @SuppressWarnings("nls")
    protected void createIndex(JestClient client, String indexName, String settingsName) throws Exception {
        URL settings = AbstractClientFactory.class.getResource(settingsName);
        String source = IOUtils.toString(settings);
        JestResult response = client.execute(new CreateIndex.Builder(indexName).settings(source).build());
        if (!response.isSucceeded()) {
            // When running in e.g. Wildfly, the Gateway exists as two separate WARs - the API and the
            // runtime Gateway itself.  They both create a registry and thus they both try to initialize
            // the ES index if it doesn't exist.  A race condition could result in both WARs trying to
            // create the index.  So a result of "IndexAlreadyExistsException" should be ignored.
            if (!indexAlreadyExistsException(response)) {
                throw new Exception("Failed to create index: '" + indexName + "' Reason: " + response.getErrorMessage());
            }
        }
    }

    @SuppressWarnings("nls")
    private boolean indexAlreadyExistsException(JestResult response) {
        // ES 1.x
        if (response.getErrorMessage().startsWith("IndexAlreadyExistsException")) {
            return true;
        }

        // ES 5.x
        // {"root_cause":[{"type":"index_already_exists_exception","reason": "..."]}}
        JsonArray causes = response.getJsonObject().getAsJsonArray("root_cause");
        for (JsonElement elem : causes) {
            if (elem.isJsonObject()) {
                JsonElement type = elem.getAsJsonObject().get("type");
                if (type != null && type.getAsString().equals("index_already_exists_exception")) {
                    return true;
                }
            }
        }
        return false;
    }

}

@@ -1037,16 +1037,51 @@
 Element;
+%0Aimport com.google.gson.JsonObject;
 %0A%0A/**%0A *
@@ -3767,24 +3767,26 @@
    %7D%0A    %7D%0A%0A
+%0A%0A
     @Suppres
@@ -4039,16 +4039,26 @@
       //
+ %7B%22error%22:
  %7B%22root_
@@ -4121,16 +4121,17 @@
 %22: %22...%22
+%7D
 %5D%7D%7D%0A    
@@ -4138,51 +4138,397 @@
     
-JsonArray causes = response.getJsonObject()
+if (response.getJsonObject() == null %7C%7C !response.getJsonObject().has(%22error%22)) %7B%0A            return false;%0A        %7D%0A%0A        // Error must be a JSON object.%0A        JsonObject error = response.getJsonObject().getAsJsonObject(%22error%22);%0A        if (!(error.has(%22root_cause%22) && error.get(%22root_cause%22).isJsonArray())) %7B%0A            return false;%0A        %7D%0A%0A        JsonArray causes = error
 .get
@@ -4550,24 +4550,25 @@
 ot_cause%22);%0A
+%0A
         for 

Handle unexpected error message format in Jest responses