/*
 * 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.ql.expression.function;

import org.elasticsearch.xpack.ql.expression.Expression;
import org.elasticsearch.xpack.ql.tree.AbstractNodeTestCase;
import org.elasticsearch.xpack.ql.tree.Source;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;

import static java.util.Collections.singletonList;
import static org.elasticsearch.xpack.ql.expression.UnresolvedAttributeTests.randomUnresolvedAttribute;
import static org.elasticsearch.xpack.ql.tree.SourceTests.randomSource;

public class UnresolvedFunctionTests extends AbstractNodeTestCase<UnresolvedFunction, Expression> {

    public UnresolvedFunction randomUnresolvedFunction() {
        /* Pick an UnresolvedFunction where the name and the
         * message don't happen to be the same String. If they
         * matched then transform would get them confused. */
        Source source = randomSource();
        String name = randomAlphaOfLength(5);
        FunctionResolutionStrategy resolutionStrategy = randomFrom(resolutionStrategies());
        List<Expression> args = randomFunctionArgs();
        boolean analyzed = randomBoolean();
        String unresolvedMessage = randomUnresolvedMessage();
        return new UnresolvedFunction(source, name, resolutionStrategy, args, analyzed, unresolvedMessage);
    }

    protected List<FunctionResolutionStrategy> resolutionStrategies() {
        return singletonList(FunctionResolutionStrategy.DEFAULT);
    }

    private static List<Expression> randomFunctionArgs() {
        // At this point we only support functions with 0, 1, or 2 arguments.
        Supplier<List<Expression>> option = randomFrom(Arrays.asList(
            Collections::emptyList,
            () -> singletonList(randomUnresolvedAttribute()),
            () -> Arrays.asList(randomUnresolvedAttribute(), randomUnresolvedAttribute())
        ));
        return option.get();
    }

    /**
     * Pick a random value for the unresolved message.
     * It is important that this value is not the same
     * as the value for the name for tests like the {@link #testTransform}
     * and for general ease of reading.
     */
    private static String randomUnresolvedMessage() {
        return randomBoolean() ? null : randomAlphaOfLength(6);
    }

    @Override
    protected UnresolvedFunction randomInstance() {
        return randomUnresolvedFunction();
    }

    @Override
    protected UnresolvedFunction mutate(UnresolvedFunction uf) {
        Supplier<UnresolvedFunction> option = randomFrom(Arrays.asList(
            () -> new UnresolvedFunction(uf.source(), randomValueOtherThan(uf.name(), () -> randomAlphaOfLength(5)),
                uf.resolutionStrategy(), uf.children(), uf.analyzed(), uf.unresolvedMessage()),
            () -> new UnresolvedFunction(uf.source(), uf.name(),
                randomValueOtherThan(uf.resolutionStrategy(), () -> randomFrom(resolutionStrategies())),
                uf.children(), uf.analyzed(), uf.unresolvedMessage()),
            () -> new UnresolvedFunction(uf.source(), uf.name(), uf.resolutionStrategy(),
                randomValueOtherThan(uf.children(), UnresolvedFunctionTests::randomFunctionArgs),
                uf.analyzed(), uf.unresolvedMessage()),
            () -> new UnresolvedFunction(uf.source(), uf.name(), uf.resolutionStrategy(), uf.children(),
                !uf.analyzed(), uf.unresolvedMessage()),
            () -> new UnresolvedFunction(uf.source(), uf.name(), uf.resolutionStrategy(), uf.children(),
                uf.analyzed(), randomValueOtherThan(uf.unresolvedMessage(), () -> randomAlphaOfLength(5)))
        ));
        return option.get();
    }

    @Override
    protected UnresolvedFunction copy(UnresolvedFunction uf) {
        return new UnresolvedFunction(uf.source(), uf.name(), uf.resolutionStrategy(), uf.children(),
            uf.analyzed(), uf.unresolvedMessage());
    }

    @Override
    public void testTransform() {
        UnresolvedFunction uf = randomUnresolvedFunction();

        String newName = randomValueOtherThan(uf.name(), () -> randomAlphaOfLength(5));
        assertEquals(new UnresolvedFunction(uf.source(), newName, uf.resolutionStrategy(), uf.children(),
                uf.analyzed(), uf.unresolvedMessage()),
            uf.transformPropertiesOnly(Object.class, p -> Objects.equals(p, uf.name()) ? newName : p));

        if (resolutionStrategies().size() > 1) {
            FunctionResolutionStrategy newResolution = randomValueOtherThan(uf.resolutionStrategy(),
                () -> randomFrom(resolutionStrategies()));
            assertEquals(new UnresolvedFunction(uf.source(), uf.name(), newResolution, uf.children(),
                    uf.analyzed(), uf.unresolvedMessage()),
                uf.transformPropertiesOnly(Object.class, p -> Objects.equals(p, uf.resolutionStrategy()) ? newResolution : p));
        }
        String newUnresolvedMessage = randomValueOtherThan(uf.unresolvedMessage(), UnresolvedFunctionTests::randomUnresolvedMessage);
        assertEquals(new UnresolvedFunction(uf.source(), uf.name(), uf.resolutionStrategy(), uf.children(),
                uf.analyzed(), newUnresolvedMessage),
            uf.transformPropertiesOnly(Object.class, p -> Objects.equals(p, uf.unresolvedMessage()) ? newUnresolvedMessage : p));

        assertEquals(new UnresolvedFunction(uf.source(), uf.name(), uf.resolutionStrategy(), uf.children(),
                !uf.analyzed(), uf.unresolvedMessage()),
            uf.transformPropertiesOnly(Object.class, p -> Objects.equals(p, uf.analyzed()) ? !uf.analyzed() : p));

    }

    @Override
    public void testReplaceChildren() {
        UnresolvedFunction uf = randomUnresolvedFunction();

        List<Expression> newChildren = randomValueOtherThan(uf.children(), UnresolvedFunctionTests::randomFunctionArgs);
        assertEquals(new UnresolvedFunction(uf.source(), uf.name(), uf.resolutionStrategy(), newChildren,
                uf.analyzed(), uf.unresolvedMessage()),
            uf.replaceChildren(newChildren));
    }
}

@@ -462,33 +462,8 @@
 e;%0A%0A
-import java.util.Arrays;%0A
 impo
@@ -488,16 +488,16 @@
 ctions;%0A
+
 import j
@@ -574,16 +574,55 @@
 plier;%0A%0A
+import static java.util.Arrays.asList;%0A
 import s
@@ -1727,25 +1727,18 @@
  return 
+a
 s
-ingleton
 List(Fun
@@ -1768,16 +1768,62 @@
 .DEFAULT
+, new FunctionResolutionStrategy() %7B%0A        %7D
 );%0A    %7D
@@ -2008,39 +2008,32 @@
 on = randomFrom(
-Arrays.
 asList(%0A        
@@ -2140,23 +2140,16 @@
   () -%3E 
-Arrays.
 asList(r
@@ -2871,23 +2871,16 @@
 domFrom(
-Arrays.
 asList(%0A
@@ -4704,62 +4704,8 @@
 ));%0A
-%0A        if (resolutionStrategies().size() %3E 1) %7B%0A    
     
@@ -4805,28 +4805,24 @@
             
-    
 () -%3E random
@@ -4856,28 +4856,24 @@
 ));%0A        
-  
-  
 assertEquals
@@ -4958,36 +4958,32 @@
                 
-    
 uf.analyzed(), u
@@ -4998,36 +4998,32 @@
 lvedMessage()),%0A
-    
             uf.t
@@ -5134,18 +5134,8 @@
 ));%0A
-        %7D%0A
     

Fix failing UnresolvedFunctionTest