/*
 * Copyright The WildFly Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.jboss.as.test.integration.domain.suites;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.helpers.domain.DomainClient;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.integration.domain.management.util.DomainTestSupport;
import org.jboss.as.test.integration.management.extension.EmptySubsystemParser;
import org.jboss.as.test.integration.management.extension.ExtensionUtils;
import org.jboss.as.test.integration.management.extension.optypes.OpTypesExtension;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.dmr.ModelNode;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.wildfly.core.testrunner.ManagementClient;

/**
 * Tests invocations of private and hidden operations.
 *
 * @author Brian Stansberry
 */
public class PrivateHiddenOperationsTestCase {

    private static final PathAddress PRIMARY = PathAddress.pathAddress(ModelDescriptionConstants.HOST, "primary");
    private static final PathAddress SECONDARY = PathAddress.pathAddress(ModelDescriptionConstants.HOST, "secondary");

    private static final PathAddress EXT = PathAddress.pathAddress("extension", OpTypesExtension.EXTENSION_NAME);
    private static final PathAddress PROFILE = PathAddress.pathAddress("profile", "default");
    private static final PathElement SUBSYSTEM = PathElement.pathElement("subsystem", OpTypesExtension.SUBSYSTEM_NAME);
    private static final PathElement MAIN_ONE = PathElement.pathElement("server", "main-one");
    private static final PathElement MAIN_THREE = PathElement.pathElement("server", "main-three");

    private static DomainTestSupport testSupport;
    private static ManagementClient managementClient;

    @BeforeClass
    public static void setupDomain() throws Exception {
        testSupport = DomainTestSuite.createSupport(PrivateHiddenOperationsTestCase.class.getSimpleName());
        DomainClient primaryClient = testSupport.getDomainPrimaryLifecycleUtil().getDomainClient();
        managementClient = new ManagementClient(primaryClient, TestSuiteEnvironment.getServerAddress(), 9090, "remoting+http");


        ExtensionUtils.createExtensionModule(OpTypesExtension.EXTENSION_NAME, OpTypesExtension.class,
                EmptySubsystemParser.class.getPackage());

        executeOp(Util.createAddOperation(EXT), SUCCESS);
        executeOp(Util.createAddOperation(PROFILE.append(SUBSYSTEM)), SUCCESS);

        executeOp(Util.createAddOperation(PRIMARY.append(EXT)), SUCCESS);
        executeOp(Util.createAddOperation(PRIMARY.append(SUBSYSTEM)), SUCCESS);

        executeOp(Util.createAddOperation(SECONDARY.append(EXT)), SUCCESS);
        executeOp(Util.createAddOperation(SECONDARY.append(SUBSYSTEM)), SUCCESS);
    }

    @AfterClass
    public static void tearDownDomain() throws IOException {
        Throwable t = null;
        List<ModelNode> ops = new ArrayList<>();
        ops.add(Util.createRemoveOperation(PRIMARY.append(SUBSYSTEM)));
        ops.add(Util.createRemoveOperation(PRIMARY.append(EXT)));
        ops.add(Util.createRemoveOperation(SECONDARY.append(SUBSYSTEM)));
        ops.add(Util.createRemoveOperation(SECONDARY.append(EXT)));
        ops.add(Util.createRemoveOperation(PROFILE.append(SUBSYSTEM)));
        ops.add(Util.createRemoveOperation(EXT));
        for (ModelNode op : ops) {
            try {
                executeOp(op, SUCCESS);
            } catch (IOException | AssertionError e) {
                if (t == null) {
                    t = e;
                }
            }
        }

        ExtensionUtils.deleteExtensionModule(OpTypesExtension.EXTENSION_NAME);

        testSupport = null;
        managementClient = null;
        DomainTestSuite.stopSupport();

        if (t instanceof IOException) {
            throw (IOException) t;
        } else if (t instanceof AssertionError) {
            throw (Error) t;
        }
    }

    @Test
    public void testDomainLevel() throws IOException {
        testPrivateHiddenOps(PROFILE, false);
    }

    @Test
    public void testDomainCallsPrivateServer() throws IOException {
        testPrivateOp(PROFILE, true, SUCCESS);
    }

    @Test
    public void testPrimaryServer() throws IOException {
        testPrivateHiddenOps(PRIMARY.append(MAIN_ONE), true);
    }

    @Test
    public void testSecondaryServer() throws IOException {
        testPrivateHiddenOps(SECONDARY.append(MAIN_THREE), true);
    }

    @Test
    public void testPrimaryHC() throws IOException {
        testPrivateHiddenOps(PRIMARY, false);
    }

    @Test
    public void testSecondaryHC() throws IOException {
        testPrivateHiddenOps(SECONDARY, false);
    }

    private void testPrivateHiddenOps(PathAddress base, boolean domain) throws IOException {
        testHiddenOp(base, domain);
        testPrivateOp(base, domain, FAILED);
    }

    private void testHiddenOp(PathAddress base, boolean domain) throws IOException {
        PathAddress target = base.append(SUBSYSTEM);
        String prefix = domain ? "domain-" : "";
        executeOp(Util.createEmptyOperation(prefix + "hidden", target), SUCCESS);
    }

    private void testPrivateOp(PathAddress base, boolean domain, String outcome) throws IOException {
        PathAddress target = base.append(SUBSYSTEM);
        String prefix = domain ? "domain-" : "";
        executeOp(Util.createEmptyOperation(prefix + "private", target), outcome);
    }

    private static void executeOp(ModelNode op, String outcome) throws IOException {
        ModelNode response = managementClient.getControllerClient().execute(op);
        assertTrue(response.toString(), response.hasDefined(OUTCOME));
        assertEquals(response.toString(), outcome, response.get(OUTCOME).asString());
    }
}
