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

package org.jboss.as.test.integration.mgmt.access;

import static org.jboss.as.test.integration.management.rbac.RbacUtil.ADMINISTRATOR_ROLE;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.AUDITOR_ROLE;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.DEPLOYER_ROLE;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.MAINTAINER_ROLE;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.MONITOR_ROLE;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.OPERATOR_ROLE;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.SUPERUSER_ROLE;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.addRoleMapping;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.addRoleUser;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.allStandardRoles;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.assertIsCallerInRole;
import static org.jboss.as.test.integration.management.rbac.RbacUtil.removeRoleUser;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import jakarta.inject.Inject;

import org.jboss.as.controller.client.ModelControllerClient;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.core.testrunner.ServerControl;
import org.wildfly.core.testrunner.ServerController;
import org.wildfly.core.testrunner.WildFlyRunner;

/**
 * This class is also used in the LDAP test cases.
 *
 * @author Ladislav Thon <lthon@redhat.com>
 */
@RunWith(WildFlyRunner.class)
@ServerControl(manual = true)
abstract class AbstractPropertiesRoleMappingTestCase extends AbstractRbacTestCase {
    @Inject
    static ServerController container;

    static BasicExtensionSetupTask setupTask = new BasicExtensionSetupTask();

    protected static void startServer() throws Exception {
        Assert.assertNotNull(container);
        container.start();
        managementClient = container.getClient();
        setupTask.setup(managementClient);
    }

    protected static void stopServer() throws Exception {
        try {
            Assert.assertNotNull(managementClient);
            setupTask.tearDown(managementClient);
            managementClient.close();
            Assert.assertNotNull(container);
            container.stop();
        } finally {
            setupTask = null;
            managementClient = null;
            container = null;
        }
    }

    @Test
    public void testMonitor() throws Exception {
        test("UserMappedToGroupMonitor", MONITOR_ROLE);
    }

    @Test
    public void testOperator() throws Exception {
        test("UserMappedToGroupOperator", OPERATOR_ROLE);
    }

    @Test
    public void testMaintainer() throws Exception {
        test("UserMappedToGroupMaintainer", MAINTAINER_ROLE);
    }

    @Test
    public void testDeployer() throws Exception {
        test("UserMappedToGroupDeployer", DEPLOYER_ROLE);
    }

    @Test
    public void testAdministrator() throws Exception {
        test("UserMappedToGroupAdministrator", ADMINISTRATOR_ROLE);
    }

    @Test
    public void testAuditor() throws Exception {
        test("UserMappedToGroupAuditor", AUDITOR_ROLE);
    }

    @Test
    public void testSuperUser() throws Exception {
        test("UserMappedToGroupSuperUser", SUPERUSER_ROLE);
    }

    @Test
    public void testOperatorAndMonitor() throws Exception {
        test("UserMappedToGroupOperatorAndMonitor", OPERATOR_ROLE, MONITOR_ROLE);
    }

    @Test
    public void testMaintainerAndMonitor() throws Exception {
        test("UserMappedToGroupMaintainerAndMonitor", MAINTAINER_ROLE, MONITOR_ROLE);
    }

    @Test
    public void testDeployerAndMonitor() throws Exception {
        test("UserMappedToGroupDeployerAndMonitor", DEPLOYER_ROLE, MONITOR_ROLE);
    }

    @Test
    public void testAdministratorAndMonitor() throws Exception {
        test("UserMappedToGroupAdministratorAndMonitor", ADMINISTRATOR_ROLE, MONITOR_ROLE);
    }

    @Test
    public void testAuditorAndMonitor() throws Exception {
        test("UserMappedToGroupAuditorAndMonitor", AUDITOR_ROLE, MONITOR_ROLE);
    }

    @Test
    public void testSuperUserAndMonitor() throws Exception {
        test("UserMappedToGroupSuperUserAndMonitor", SUPERUSER_ROLE, MONITOR_ROLE);
    }

    @Test
    public void testOperatorAndMonitorAndExcludedFromMonitor() throws Exception {
        test("UserMappedToGroupOperatorAndMonitorAndExcludedFromGroupMonitor", OPERATOR_ROLE);
    }

    @Test
    public void testMaintainerAndMonitorAndExcludedFromMonitor() throws Exception {
        test("UserMappedToGroupMaintainerAndMonitorAndExcludedFromGroupMonitor", MAINTAINER_ROLE);
    }

    @Test
    public void testDeployerAndMonitorAndExcludedFromMonitor() throws Exception {
        test("UserMappedToGroupDeployerAndMonitorAndExcludedFromGroupMonitor", DEPLOYER_ROLE);
    }

    @Test
    public void testAdministratorAndMonitorAndExcludedFromMonitor() throws Exception {
        test("UserMappedToGroupAdministratorAndMonitorAndExcludedFromGroupMonitor", ADMINISTRATOR_ROLE);
    }

    @Test
    public void testAuditorAndMonitorAndExcludedFromMonitor() throws Exception {
        test("UserMappedToGroupAuditorAndMonitorAndExcludedFromGroupMonitor", AUDITOR_ROLE);
    }

    @Test
    public void testSuperUserAndMonitorAndExcludedFromMonitor() throws Exception {
        test("UserMappedToGroupSuperUserAndMonitorAndExcludedFromGroupMonitor", SUPERUSER_ROLE);
    }

    @Test
    public void testOperatorAndMonitorAndExcludingGroup() throws Exception {
        test("UserMappedToGroupOperatorAndMonitorAndExcludingGroup", OPERATOR_ROLE);
    }

    @Test
    public void testMaintainerAndMonitorAndExcludingGroup() throws Exception {
        test("UserMappedToGroupMaintainerAndMonitorAndExcludingGroup", MAINTAINER_ROLE);
    }

    @Test
    public void testDeployerAndMonitorAndExcludingGroup() throws Exception {
        test("UserMappedToGroupDeployerAndMonitorAndExcludingGroup", DEPLOYER_ROLE);
    }

    @Test
    public void testAdministratorAndMonitorAndExcludingGroup() throws Exception {
        test("UserMappedToGroupAdministratorAndMonitorAndExcludingGroup", ADMINISTRATOR_ROLE);
    }

    @Test
    public void testAuditorAndMonitorAndExcludingGroup() throws Exception {
        test("UserMappedToGroupAuditorAndMonitorAndExcludingGroup", AUDITOR_ROLE);
    }

    @Test
    public void testSuperUserAndMonitorAndExcludingGroup() throws Exception {
        test("UserMappedToGroupSuperUserAndMonitorAndExcludingGroup", SUPERUSER_ROLE);
    }

    @Test
    public void testRuntimeReconfigurationMonitor() throws Exception {
        testRuntimeReconfiguration("UserMappedToGroupMonitor", MONITOR_ROLE);
    }

    @Test
    public void testRuntimeReconfigurationOperator() throws Exception {
        testRuntimeReconfiguration("UserMappedToGroupOperator", OPERATOR_ROLE);
    }

    @Test
    public void testRuntimeReconfigurationMaintainer() throws Exception {
        testRuntimeReconfiguration("UserMappedToGroupMaintainer", MAINTAINER_ROLE);
    }

    @Test
    public void testRuntimeReconfigurationDeployer() throws Exception {
        testRuntimeReconfiguration("UserMappedToGroupDeployer", DEPLOYER_ROLE);
    }

    @Test
    public void testRuntimeReconfigurationAdministrator() throws Exception {
        testRuntimeReconfiguration("UserMappedToGroupAdministrator", ADMINISTRATOR_ROLE);
    }

    @Test
    public void testRuntimeReconfigurationAuditor() throws Exception {
        testRuntimeReconfiguration("UserMappedToGroupAuditor", AUDITOR_ROLE);
    }

    @Test
    public void testRuntimeReconfigurationSuperUser() throws Exception {
        testRuntimeReconfiguration("UserMappedToGroupSuperUser", SUPERUSER_ROLE);
    }

    private void test(String user, String... expectedRoles) throws IOException {
        Set<String> expectedRolesSet = new HashSet<String>(Arrays.asList(expectedRoles));

        ModelControllerClient client = getClientForUser(user);
        for (String role : allStandardRoles()) {
            assertIsCallerInRole(client, role, expectedRolesSet.contains(role));
        }
    }

    private void testRuntimeReconfiguration(String user, String originalRole) throws Exception {
        Set<String> allRolesWithoutTheOriginal = new HashSet<String>(Arrays.asList(allStandardRoles()));
        allRolesWithoutTheOriginal.remove(originalRole);

        for (String newRole : allRolesWithoutTheOriginal) {
            test(user, originalRole);
            addUserToRole(user, newRole);
            try {
                test(user, originalRole, newRole);
            } finally {
                removeUserFromRole(user, newRole);
            }
            test(user, originalRole);
        }
    }

    // test utils

    private void addUserToRole(String user, String role) throws IOException {
        ModelControllerClient client = getManagementClient().getControllerClient();
        addRoleMapping(role, client); // make sure the role mapping exists
        addRoleUser(role, user, client);
    }

    private void removeUserFromRole(String user, String role) throws IOException {
        ModelControllerClient client = getManagementClient().getControllerClient();
        addRoleMapping(role, client); // make sure the role mapping exists
        removeRoleUser(role, user, client);
    }
}
