/*
 * 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.brooklyn.entity.webapp;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;

import java.util.List;
import java.util.concurrent.Callable;

import org.apache.brooklyn.core.entity.EntityAsserts;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.test.HttpTestUtils;
import org.apache.brooklyn.test.entity.TestJavaWebAppEntity;
import org.apache.brooklyn.test.support.TestResourceUnavailableException;
import org.apache.brooklyn.util.collections.CollectionFunctionals;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.RecordingSensorEventListener;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
import org.apache.brooklyn.entity.proxy.AbstractController;
import org.apache.brooklyn.entity.proxy.LoadBalancer;
import org.apache.brooklyn.entity.proxy.nginx.NginxController;
import org.apache.brooklyn.entity.webapp.tomcat.TomcatServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;

import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;

public class ControlledDynamicWebAppClusterIntegrationTest extends BrooklynAppLiveTestSupport {
    private static final Logger log = LoggerFactory.getLogger(ControlledDynamicWebAppClusterIntegrationTest.class);

    private static final int TIMEOUT_MS = 10*1000;
    
    private LocalhostMachineProvisioningLocation loc;
    private List<LocalhostMachineProvisioningLocation> locs;
    
    @Override
    @BeforeMethod(alwaysRun=true)
    public void setUp() throws Exception {
        super.setUp();

        loc = app.newLocalhostProvisioningLocation();
        locs = ImmutableList.of(loc);
    }

    public String getTestWar() {
        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
        return "classpath://hello-world.war";
    }
    
    @Test(groups="Integration")
    public void testPropogateHttpPorts() {
        ControlledDynamicWebAppCluster cluster = app.createAndManageChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
                .configure("initialSize", 1));
        app.start(locs);

        EntityAsserts.assertAttributeEventuallyNonNull(cluster, LoadBalancer.PROXY_HTTP_PORT);
        EntityAsserts.assertAttributeEventuallyNonNull(cluster, LoadBalancer.PROXY_HTTPS_PORT);
    }
    
    @Test(groups="Integration")
    public void testConfiguresController() {
        ControlledDynamicWebAppCluster cluster = app.createAndManageChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
                .configure("initialSize", 1)
                .configure("memberSpec", EntitySpec.create(TomcatServer.class).configure("war", getTestWar())));
        app.start(locs);

        String url = cluster.getController().getAttribute(NginxController.ROOT_URL);
        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(url, 200);
        HttpTestUtils.assertContentEventuallyContainsText(url, "Hello");
    }
    
    @Test(groups="Integration")
    public void testSetsToplevelHostnameFromController() {
        ControlledDynamicWebAppCluster cluster = app.createAndManageChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
                .configure("initialSize", 1)
                .configure("memberSpec", EntitySpec.create(TomcatServer.class).configure("war", getTestWar())));
        app.start(locs);

        String expectedHostname = cluster.getController().getAttribute(LoadBalancer.HOSTNAME);
        String expectedRootUrl = cluster.getController().getAttribute(LoadBalancer.ROOT_URL);
        boolean expectedServiceUp = true;
        
        assertNotNull(expectedHostname);
        assertNotNull(expectedRootUrl);
        
        EntityAsserts.assertAttributeEqualsEventually(MutableMap.of("timeout", TIMEOUT_MS), cluster, ControlledDynamicWebAppCluster.HOSTNAME, expectedHostname);
        EntityAsserts.assertAttributeEqualsEventually(MutableMap.of("timeout", TIMEOUT_MS), cluster, ControlledDynamicWebAppCluster.ROOT_URL, expectedRootUrl);
        EntityAsserts.assertAttributeEqualsEventually(MutableMap.of("timeout", TIMEOUT_MS), cluster, ControlledDynamicWebAppCluster.SERVICE_UP, expectedServiceUp);
    }
    
    @Test(groups="Integration")
    public void testCustomWebClusterSpecGetsMemberSpec() {
        ControlledDynamicWebAppCluster cluster = app.createAndManageChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
                .configure("initialSize", 1)
                .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(TomcatServer.class)
                        .configure(TomcatServer.ROOT_WAR, getTestWar()))
                .configure(ControlledDynamicWebAppCluster.WEB_CLUSTER_SPEC, EntitySpec.create(DynamicWebAppCluster.class)
                        .displayName("mydisplayname")));
        app.start(locs);

        String url = cluster.getController().getAttribute(NginxController.ROOT_URL);
        HttpTestUtils.assertContentEventuallyContainsText(url, "Hello");

        // and make sure it really was using our custom spec
        assertEquals(cluster.getCluster().getDisplayName(), "mydisplayname");
    }
    
    // Needs to be integration test because still using nginx controller; could pass in mock controller
    @Test(groups="Integration")
    public void testSetsServiceLifecycle() {
        ControlledDynamicWebAppCluster cluster = app.createAndManageChild( EntitySpec.create(ControlledDynamicWebAppCluster.class)
                .configure("initialSize", 1)
                .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(TestJavaWebAppEntity.class)) );
        
        EntityAsserts.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED);
        
        RecordingSensorEventListener<Lifecycle> listener = new RecordingSensorEventListener<Lifecycle>(true);
        app.subscriptions().subscribe(cluster, Attributes.SERVICE_STATE_ACTUAL, listener);
        app.start(locs);
        
        Asserts.eventually(Suppliers.ofInstance(listener.getEventValues()), CollectionFunctionals.sizeEquals(2));
        assertEquals(listener.getEventValues(), ImmutableList.of(Lifecycle.STARTING, Lifecycle.RUNNING), "vals="+listener.getEventValues());
        listener.clearEvents();
        
        app.stop();
        EntityAsserts.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED);
        Asserts.eventually(Suppliers.ofInstance(listener), CollectionFunctionals.sizeEquals(2));
        assertEquals(listener.getEventValues(), ImmutableList.of(Lifecycle.STOPPING, Lifecycle.STOPPED), "vals="+listener.getEventValues());
    }
    
    @Test(groups="Integration")
    public void testTomcatAbsoluteRedirect() {
        final ControlledDynamicWebAppCluster cluster = app.createAndManageChild(EntitySpec.create(ControlledDynamicWebAppCluster.class)
            .configure(ControlledDynamicWebAppCluster.MEMBER_SPEC, EntitySpec.create(TomcatServer.class)
            .configure(TomcatServer.ROOT_WAR, getTestWar()))
            .configure("initialSize", 1)
            .configure(AbstractController.SERVICE_UP_URL_PATH, "hello/redirectAbsolute")
        );
        app.start(locs);

        final NginxController nginxController = (NginxController) cluster.getController();
        Asserts.succeedsEventually(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return nginxController.getServerPoolAddresses().size() == 1;
            }
        });
        
        Entity tomcatServer = Iterables.getOnlyElement(cluster.getCluster().getMembers());
        EntityAsserts.assertAttributeEqualsEventually(tomcatServer, Attributes.SERVICE_UP, true);
        
        EntityAsserts.assertAttributeEqualsContinually(nginxController, Attributes.SERVICE_UP, true);
        
        app.stop();
    }
}
