/**
 * 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.activemq;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.management.ObjectName;
import org.apache.activemq.network.DiscoveryNetworkConnector;
import org.apache.activemq.network.NetworkConnector;
import org.apache.activemq.plugin.RuntimeConfigurationBroker;
import org.apache.activemq.plugin.jmx.RuntimeConfigurationViewMBean;
import org.apache.activemq.util.IntrospectionSupport;
import org.apache.activemq.util.Wait;
import org.junit.Ignore;
import org.junit.Test;


import static org.junit.Assert.*;

public class SpringBeanTest extends RuntimeConfigTestSupport {

    @Test
    @Ignore("This fail on Jenkins but works fine locally")
    public void testModifiable() throws Exception {
        final String brokerConfig =  "SpringBeanTest-broker";
        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-bean");
        startBroker(brokerConfig);
        assertTrue("broker alive", brokerService.isStarted());

        // apply via jmx
        ObjectName objectName =
                new ObjectName(brokerService.getBrokerObjectName().toString() +
                        RuntimeConfigurationBroker.objectNamePropsAppendage);
        RuntimeConfigurationViewMBean runtimeConfigurationView =
                (RuntimeConfigurationViewMBean) brokerService.getManagementContext().newProxyInstance(objectName,
                        RuntimeConfigurationViewMBean.class, false);

        String propOfInterest = "modified";
        HashMap<String, String> props = new HashMap<String, String>();
        IntrospectionSupport.getProperties(runtimeConfigurationView, props, null);
        LOG.info("mbean attributes before: " + props);

        assertNotEquals("unknown", props.get(propOfInterest));

        String result = runtimeConfigurationView.updateNow();
        
        LOG.info("Result from update: " + result);

        assertTrue("got sensible result", result.contains("No material change"));

        HashMap<String, String> propsAfter = new HashMap<String, String>();
        IntrospectionSupport.getProperties(runtimeConfigurationView, propsAfter, null);
        LOG.info("mbean attributes after: " + propsAfter);

        assertEquals("modified is same", props.get(propOfInterest), propsAfter.get(propOfInterest));
    }


    @Test
    public void testAddPropertyRef() throws Exception {

        System.setProperty("network.uri", "static:(tcp://localhost:8888)");
        final String brokerConfig = "SpringPropertyTest-broker";
        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property");
        startBroker(brokerConfig);
        assertTrue("broker alive", brokerService.isStarted());

        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property-nc", SLEEP);

        assertTrue("new network connectors", Wait.waitFor(new Wait.Condition() {
            @Override
            public boolean isSatisified() throws Exception {
                return 1 == brokerService.getNetworkConnectors().size();
            }
        }));

        DiscoveryNetworkConnector discoveryNetworkConnector =
                (DiscoveryNetworkConnector) brokerService.getNetworkConnectors().get(0);
        assertEquals("property replaced", System.getProperty("network.uri"), discoveryNetworkConnector.getUri().toASCIIString());
    }

    @Test
    public void testAddPropertyRefFromFile() throws Exception {

        System.setProperty("network.uri", "static:(tcp://localhost:8888)");
        System.setProperty("props.base", "classpath:");
        final String brokerConfig = "SpringPropertyTest-broker";
        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property-file");
        startBroker(brokerConfig);
        assertTrue("broker alive", brokerService.isStarted());

        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property-file-nc", SLEEP);

        assertTrue("new network connectors", Wait.waitFor(new Wait.Condition() {
            @Override
            public boolean isSatisified() throws Exception {
                return 1 == brokerService.getNetworkConnectors().size();
            }
        }));

        DiscoveryNetworkConnector discoveryNetworkConnector =
                (DiscoveryNetworkConnector) brokerService.getNetworkConnectors().get(0);
        assertEquals("property replaced", System.getProperty("network.uri"), discoveryNetworkConnector.getUri().toASCIIString());

        assertEquals("name is replaced", "guest", discoveryNetworkConnector.getName());
    }

    @Test
    public void testAddPropertyRefFromFileAsList() throws Exception {

        System.setProperty("network.uri", "static:(tcp://localhost:8888)");
        System.setProperty("props.base", "classpath:");
        final String brokerConfig = "SpringPropertyTestFileList-broker";
        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property-file-list");
        startBroker(brokerConfig);
        assertTrue("broker alive", brokerService.isStarted());

        ObjectName objectName =
                new ObjectName(brokerService.getBrokerObjectName().toString() +
                        RuntimeConfigurationBroker.objectNamePropsAppendage);
        RuntimeConfigurationViewMBean runtimeConfigurationView =
                (RuntimeConfigurationViewMBean) brokerService.getManagementContext().newProxyInstance(objectName,
                        RuntimeConfigurationViewMBean.class, false);

        String propOfInterest = "modified";
        HashMap<String, String> props = new HashMap<String, String>();
        IntrospectionSupport.getProperties(runtimeConfigurationView, props, null);
        LOG.info("mbean attributes before: " + props);

        assertNotEquals("unknown", props.get(propOfInterest));


    }

    @Test
    public void testRemovePropertyRefNetworkConnector() throws Exception {

        System.setProperty("network.uri", "static:(tcp://localhost:8888)");
        System.setProperty("network.user", "guest");
        System.setProperty("network.password", "secret");

        final String brokerConfig = "SpringPropertyRemoveTest-broker";
        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property-two-nc");
        startBroker(brokerConfig);
        assertTrue("broker alive", brokerService.isStarted());
        assertEquals("two network connectors", 2, brokerService.getNetworkConnectors().size());

        for (DiscoveryNetworkConnector nc : asDiscoveryConnectors(brokerService.getNetworkConnectors())) {
            assertEquals("uri resolved", System.getProperty("network.uri"), nc.getUri().toASCIIString());
            assertEquals("userName resolved", System.getProperty("network.user"), nc.getUserName());
            assertEquals("password resolved", System.getProperty("network.password"), nc.getPassword());
        }

        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property-one-nc", SLEEP);

        assertTrue("one network connector remains", Wait.waitFor(new Wait.Condition() {
            @Override
            public boolean isSatisified() throws Exception {
                return 1 == brokerService.getNetworkConnectors().size();
            }
        }));

        DiscoveryNetworkConnector remaining = (DiscoveryNetworkConnector) brokerService.getNetworkConnectors().get(0);
        assertEquals("kept the one named 'one'", "one", remaining.getName());
        assertEquals("uri still resolved", System.getProperty("network.uri"), remaining.getUri().toASCIIString());
        assertEquals("userName still resolved", System.getProperty("network.user"), remaining.getUserName());
        assertEquals("password still resolved", System.getProperty("network.password"), remaining.getPassword());
    }

    private static List<DiscoveryNetworkConnector> asDiscoveryConnectors(List<NetworkConnector> connectors) {
        List<DiscoveryNetworkConnector> result = new ArrayList<>(connectors.size());
        for (NetworkConnector nc : connectors) {
            result.add((DiscoveryNetworkConnector) nc);
        }
        return result;
    }

    @Test
    public void testAddPropertyRefFromFileAndBeanFactory() throws Exception {

        System.setProperty("network.uri", "static:(tcp://localhost:8888)");
        System.setProperty("props.base", "classpath:");
        final String brokerConfig = "SpringPropertyTestFileListBeanFactory-broker";
        applyNewConfig(brokerConfig, "emptyUpdatableConfig1000-spring-property-file-list-and-beanFactory");
        startBroker(brokerConfig);
        assertTrue("broker alive", brokerService.isStarted());

        ObjectName objectName =
                new ObjectName(brokerService.getBrokerObjectName().toString() +
                        RuntimeConfigurationBroker.objectNamePropsAppendage);
        RuntimeConfigurationViewMBean runtimeConfigurationView =
                (RuntimeConfigurationViewMBean) brokerService.getManagementContext().newProxyInstance(objectName,
                        RuntimeConfigurationViewMBean.class, false);

        String propOfInterest = "modified";
        HashMap<String, String> props = new HashMap<String, String>();
        IntrospectionSupport.getProperties(runtimeConfigurationView, props, null);
        LOG.info("mbean attributes before: " + props);

        assertNotEquals("unknown", props.get(propOfInterest));

        assertEquals("our custom prop is applied", "isKing", brokerService.getBrokerName());

        applyNewConfig(brokerConfig, "spring-property-file-list-and-beanFactory-new-nc", SLEEP);

        assertTrue("new network connectors", Wait.waitFor(new Wait.Condition() {
            @Override
            public boolean isSatisified() throws Exception {
                return 1 == brokerService.getNetworkConnectors().size();
            }
        }));

        assertEquals("our custom prop is applied", "isKing", brokerService.getNetworkConnectors().get(0).getName());

    }

}
