package org.jboss.installer.postinstall.task.impl;

import org.jboss.installer.core.InstallationData;
import org.jboss.installer.postinstall.TaskPrinter;
import org.jboss.installer.postinstall.server.DomainServer;
import org.jboss.installer.postinstall.server.EmbeddedServer;
import org.jboss.installer.postinstall.server.StandaloneServer;
import org.jboss.installer.postinstall.task.CredentialStoreConfig;
import org.jboss.installer.postinstall.task.LDAPManagementAuthConfig;
import org.jboss.installer.postinstall.task.NoopPrinter;
import org.jboss.installer.test.utils.MockLdapServer;
import org.jboss.installer.test.utils.ServerUtils;
import org.jboss.installer.test.utils.StandaloneXmlUtil;
import org.jboss.installer.test.utils.TestServer;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.wildfly.core.launcher.ProcessHelper;

import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class LDAPSetupTaskTest {

    protected static final String LDAP_REALM_NAME = "my-ldap-realm";
    @ClassRule
    public static TestServer testServer = new TestServer();
    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();
    private MockLdapServer ldapServer;

    private StandaloneServer standaloneServer;
    private DomainServer domainServer;
    private InstallationData idata;
    private TaskPrinter printer = new NoopPrinter();
    private LDAPManagementAuthConfig config;
    private static final String LDAP_TEST_URL =  "ldap://localhost:10389";
    private static final String LDAP_ADMIN_DN = "uid=admin,ou=system";
    private static final String LDAP_ADMIN_PASSWORD = "secret";

    @Before
    public void setUp() throws Exception {
        standaloneServer = new StandaloneServer(TestServer.TARGET_PATH);
        domainServer = new DomainServer(TestServer.TARGET_PATH);
        idata = new InstallationData();
        idata.setTargetFolder(TestServer.TARGET_PATH);

        ldapServer = new MockLdapServer(tempFolder.getRoot());
        ldapServer.startServer();
        ldapServer.addUser("cn=John Brown,o=TestOrganization","jbrown","John Brown", "Brown");

        config = new LDAPManagementAuthConfig("test.connection", LDAP_TEST_URL, LDAP_ADMIN_DN, LDAP_ADMIN_PASSWORD);
        config.setRealmName(LDAP_REALM_NAME);
        config.setFilterType(LDAPManagementAuthConfig.FilterType.USERNAME);
        config.setFilter("uid");
        config.setRecursive(false);
        config.setBaseDN("o=TestOrganization");
        idata.putConfig(config);
    }

    @After
    public void tearDown() throws Exception {
        ldapServer.stopServer();
        testServer.restoreConfigs();
    }

    @Test
    public void testApplyToStandalone() throws Exception {
        try {
            standaloneServer.start("standalone.xml");
            assertTrue(new LDAPSetupTask().applyToStandalone(idata, standaloneServer, printer));
        } finally {
            standaloneServer.shutdown();
        }
        verifyCanLogIn(standaloneServer);
    }

    @Test
    public void applyToMultipleConfigTest() throws Exception {
        idata.putConfig(new CredentialStoreConfig("test-store", null, TestServer.TARGET_PATH.resolve("test.store").toString(), "abcd1234"));
        try {
            standaloneServer.start("standalone-full.xml");
            assertTrue(new CredentialStoreInstallTask().applyToStandalone(idata, standaloneServer, printer));
            assertTrue(new LDAPSetupTask().applyToStandalone(idata, standaloneServer, printer));
        } finally {
            standaloneServer.shutdown();
        }
        try {
            standaloneServer.start("standalone-ha.xml");
            assertTrue(new CredentialStoreInstallTask().applyToStandalone(idata, standaloneServer, printer));
            assertTrue(new LDAPSetupTask().applyToStandalone(idata, standaloneServer, printer));
        } finally {
            standaloneServer.shutdown();
        }
    }
    @Test
    public void testApplyToDomain() throws Exception {
        try {
            domainServer.start("host.xml");
            assertTrue(new LDAPSetupTask().applyToDomain(idata, domainServer, printer));
        } finally {
            domainServer.shutdown();
        }
        verifyCanLogIn(domainServer);
    }

    @Test
    public void testSetRecurseSearch() throws Exception {
        config.setRecursive(true);
        try {
            standaloneServer.start("standalone.xml");
            assertTrue(new LDAPSetupTask().applyToStandalone(idata, standaloneServer, printer));
        } finally {
            standaloneServer.shutdown();
        }

        final StandaloneXmlUtil standaloneXml = new StandaloneXmlUtil(TestServer.TARGET_PATH, "standalone.xml");
        final String xpath = String.format("//security-realms/ldap-realm[@name='%s']/identity-mapping", LDAP_REALM_NAME);
        final String isRecursive = standaloneXml.getAttribute(xpath, "use-recursive-search");
        assertEquals("Is recursive value should be set", isRecursive, "true");
    }

    public void verifyCanLogIn(EmbeddedServer embeddedServer) throws Exception {
        Process server = null;
        try {
            if (!embeddedServer.isDomain()) {
                server = ServerUtils.startServer(embeddedServer, "jbrown", "testpass");
            } else {
                // CLI is not using LDAP in domain mode
                server = ServerUtils.startServer(embeddedServer, "", "");
            }


            URL url = new URL("http://localhost:9990/management");
            String encoding = Base64.getEncoder().encodeToString(("jbrown:testpass").getBytes(StandardCharsets.UTF_8));

            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setDoOutput(true);
            connection.setRequestProperty("Authorization", "Basic " + encoding);
            assertEquals(200, connection.getResponseCode());
        } finally {
            if (server != null) {
                System.out.println("Shutting down ");
                ProcessHelper.destroyProcess(server);
                System.out.println("shut down");
            }
        }
    }

}