/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */
package org.elasticsearch.xpack.watcher.history;

import org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.MockScriptPlugin;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.xpack.watcher.condition.AlwaysCondition;
import org.elasticsearch.xpack.watcher.execution.ExecutionState;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static java.util.Collections.singletonMap;
import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.loggingAction;
import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder;
import static org.elasticsearch.xpack.watcher.input.InputBuilders.simpleInput;
import static org.elasticsearch.xpack.watcher.transform.TransformBuilders.scriptTransform;
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.interval;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;

/**
 * This test makes sure that the http host and path fields in the watch_record action result are
 * not analyzed so they can be used in aggregations
 */
@TestLogging("org.elasticsearch.xpack.watcher:DEBUG,org.elasticsearch.xpack.watcher.WatcherIndexingListener:TRACE")
public class HistoryTemplateTransformMappingsTests extends AbstractWatcherIntegrationTestCase {

    @Override
    protected List<Class<? extends Plugin>> pluginTypes() {
        List<Class<? extends Plugin>> types = super.pluginTypes();
        types.add(CustomScriptPlugin.class);
        return types;
    }

    public static class CustomScriptPlugin extends MockScriptPlugin {

        @Override
        @SuppressWarnings("unchecked")
        protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() {
            Map<String, Function<Map<String, Object>, Object>> scripts = new HashMap<>();

            scripts.put("return [ 'key' : 'value1' ];", vars -> singletonMap("key", "value1"));
            scripts.put("return [ 'key' : 'value2' ];", vars -> singletonMap("key", "value2"));
            scripts.put("return [ 'key' : [ 'key1' : 'value1' ] ];", vars -> singletonMap("key", singletonMap("key1", "value1")));
            scripts.put("return [ 'key' : [ 'key1' : 'value2' ] ];", vars -> singletonMap("key", singletonMap("key1", "value2")));

            return scripts;
        }

        @Override
        public String pluginScriptLang() {
            return WATCHER_LANG;
        }
    }

    @Override
    protected boolean timeWarped() {
        return true; // just to have better control over the triggers
    }

    @AwaitsFix(bugUrl = "https://github.com/elastic/x-pack-elasticsearch/issues/1517")
    public void testTransformFields() throws Exception {
        String index = "the-index";
        String type = "the-type";
        createIndex(index);
        index(index, type, "{}");
        flush();
        refresh();

        PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id1").setSource(watchBuilder()
                .trigger(schedule(interval("5s")))
                .input(simpleInput())
                .condition(AlwaysCondition.INSTANCE)
                .transform(scriptTransform("return [ 'key' : 'value1' ];"))
                .addAction("logger", scriptTransform("return [ 'key' : 'value2' ];"), loggingAction("indexed")))
                .get();
        assertThat(putWatchResponse.isCreated(), is(true));
        timeWarp().trigger("_id1");

        // adding another watch which with a transform that should conflict with the preview watch. Since the
        // mapping for the transform construct is disabled, there should be nor problems.
        putWatchResponse = watcherClient().preparePutWatch("_id2").setSource(watchBuilder()
                .trigger(schedule(interval("5s")))
                .input(simpleInput())
                .condition(AlwaysCondition.INSTANCE)
                .transform(scriptTransform("return [ 'key' : [ 'key1' : 'value1' ] ];"))
                .addAction("logger", scriptTransform("return [ 'key' : [ 'key1' : 'value2' ] ];"), loggingAction("indexed")))
                .get();
        assertThat(putWatchResponse.isCreated(), is(true));
        timeWarp().trigger("_id2");

        flush();
        refresh();

        assertWatchWithMinimumActionsCount("_id1", ExecutionState.EXECUTED, 1);
        assertWatchWithMinimumActionsCount("_id2", ExecutionState.EXECUTED, 1);

        refresh();

        assertBusy(() -> {
            GetFieldMappingsResponse response = client().admin().indices()
                                                                .prepareGetFieldMappings(".watcher-history*")
                                                                .setFields("result.actions.transform.payload")
                                                                .setTypes("doc")
                                                                .includeDefaults(true)
                                                                .get();

            for (Map<String, Map<String, GetFieldMappingsResponse.FieldMappingMetaData>> map : response.mappings().values()) {
                Map<String, GetFieldMappingsResponse.FieldMappingMetaData> watchRecord = map.get("doc");
                assertThat(watchRecord, hasKey("result.actions.transform.payload"));
                GetFieldMappingsResponse.FieldMappingMetaData fieldMappingMetaData = watchRecord.get("result.actions.transform.payload");
                assertThat(fieldMappingMetaData.isNull(), is(true));
            }
        });
    }
}

@@ -904,25 +904,87 @@
 til.
-function.Function
+Objects;%0Aimport java.util.function.Function;%0Aimport java.util.stream.Collectors
 ;%0A%0Ai
@@ -1536,16 +1536,61 @@
 terval;%0A
+import static org.hamcrest.Matchers.hasItem;%0A
 import s
@@ -3294,95 +3294,8 @@
  %7D%0A%0A
-    @AwaitsFix(bugUrl = %22https://github.com/elastic/x-pack-elasticsearch/issues/1517%22)%0A
     
@@ -5645,90 +5645,173 @@
     
-for (Map%3CString, Map%3CString, GetFieldMappingsResponse.FieldMappingMetaData%3E%3E map :
+// time might have rolled over to a new day, thus we need to check that this field exists only in one of the history indices%0A            List%3CBoolean%3E payloadNulls =
  res
@@ -5835,20 +5835,29 @@
 values()
-) %7B%0A
+.stream()%0A   
         
@@ -5868,80 +5868,20 @@
     
-Map%3CString, GetFieldMappingsResponse.FieldMappingMetaData%3E watchRecord =
+ .map(map -%3E
  map
@@ -5891,17 +5891,17 @@
 t(%22doc%22)
-;
+)
 %0A       
@@ -5913,38 +5913,31 @@
     
-assertThat(watchRecord, hasKey
+    .map(map -%3E map.get
 (%22re
@@ -5969,17 +5969,16 @@
 yload%22))
-;
 %0A       
@@ -5990,212 +5990,222 @@
     
-GetFieldMappingsResponse.FieldMappingMetaData fieldMappingMetaData = watchRecord.get(%22result.actions.transform.payload%22);%0A                assertThat(fieldMappingMetaData.isNull(), is(true));%0A            %7D
+    .filter(Objects::nonNull)%0A                    .map(GetFieldMappingsResponse.FieldMappingMetaData::isNull)%0A                    .collect(Collectors.toList());%0A%0A            assertThat(payloadNulls, hasItem(true));
 %0A   

Remove AwaitsFix from HistoryTemplateTransformMappingsTests