/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.nifi.tests.system.validation;

import org.apache.nifi.tests.system.NiFiSystemIT;
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
import org.apache.nifi.web.api.entity.ProcessorEntity;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class DependentPropertyValidationIT extends NiFiSystemIT {

    @Timeout(20)
    @Test
    public void testPropertyDependenciesAreValidatedProperly() throws NiFiClientException, IOException, InterruptedException {
        final ProcessorEntity processor = getClientUtil().createProcessor("DependOnProperties");
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Always Required", "foo"));

        getClientUtil().waitForValidProcessor(processor.getId());

        // Processor will become invalid because "Always Optional" is set and "Required If Optional Property Set" is not.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Always Optional", "hello"));
        getClientUtil().waitForInvalidProcessor(processor.getId());

        // Set the "Required If Optional Property Set" property. This should make the processor valid.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Required If Optional Property Set", "hello"));
        getClientUtil().waitForValidProcessor(processor.getId());

        // If 'Always Optional' is set to 'foo', then the 'Required If Optional Property Set To Foo' property must be set.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Always Optional", "foo"));
        getClientUtil().waitForInvalidProcessor(processor.getId());

        // Setting the 'Required If Optional Property Set To Foo' to any value will now make the processor valid.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Required If Optional Property Set To Foo", "hello"));
        getClientUtil().waitForValidProcessor(processor.getId());

        // Setting the 'Second Level Dependency' property to -42 will make the processor invalid because the property required a positive integer.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Second Level Dependency", "-42"));
        getClientUtil().waitForInvalidProcessor(processor.getId());

        // If We now set the "Always Optional" property to 'other', then the 'Second Level Dependency' property should not be validated.
        // This is because its dependency is not fully satisfied because of the 'transitive' dependency. I.e., it depends on 'Required If Optional Property Set To Foo'
        // but since 'Required If Optional Property Set To Foo' is not available/validated (because Always Optional is set to 'other'), then 'Second Level Dependency' is also not available/validated.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Always Optional", "other"));
        getClientUtil().waitForValidProcessor(processor.getId());

        // Setting the 'Always Required' property to 'bar' will result in requiring the 'Required If Always Required Is Bar Or Baz' property to be set.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Always Required", "bar"));
        getClientUtil().waitForInvalidProcessor(processor.getId());

        // Setting the 'Required If Always Required Is Bar Or Baz' to any positive integer should now make the processor valid.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Required If Always Required Is Bar Or Baz", "42"));
        getClientUtil().waitForValidProcessor(processor.getId());

        // Setting the property to -42 should result in an invalid processor because the property must be a positive integer.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Required If Always Required Is Bar Or Baz", "-42"));
        getClientUtil().waitForInvalidProcessor(processor.getId());

        // Changing the 'Always Required' back to 'foo' should result in the processor becoming valid again. Even though 'Required If Always Required Is Bar Or Baz' is invalid,
        // the processor is still valid because this property will no longer be considered.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Always Required", "foo"));
        getClientUtil().waitForValidProcessor(processor.getId());

        // Setting Always Required = foo and Always Optional = bar means that "Multiple Dependencies" will be required.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Always Optional", "bar"));
        getClientUtil().waitForInvalidProcessor(processor.getId());

        // Setting the "Multiple Dependencies" property to anything other than empty string will result in the processor becoming valid.
        getClientUtil().updateProcessorProperties(processor, Collections.singletonMap("Multiple Dependencies", "bar"));
        getClientUtil().waitForValidProcessor(processor.getId());

        // Processor should stay valid if we make Multiple Dependencies empty string (invalid) but also change Always Optional to "other" because that will result in "Multiple Dependencies"
        // no longer being available/validated.
        final Map<String, String> props = new HashMap<>();
        props.put("Multiple Dependencies", "");
        props.put("Always Optional", "other");
        getClientUtil().updateProcessorProperties(processor, props);
        getClientUtil().waitForValidProcessor(processor.getId());
    }

}
