package org.jboss.qe.camel.components.ejb;

import java.io.IOException;

import javax.inject.Inject;
import javax.jms.ConnectionFactory;
import javax.naming.InitialContext;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.arquillian.api.ServerSetup;
import org.jboss.as.arquillian.api.ServerSetupTask;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.qe.camel.components.ejb.subB.NewTransactionEjb;
import org.jboss.qe.camel.components.ejb.subB.RequiredTransactionEjb;
import org.jboss.qe.camel.components.ejb.subB.TransactedRouteCaller;
import org.jboss.qe.shared.util.jms.JmsClient;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.camel.test.common.utils.JMSUtils;
import org.wildfly.extension.camel.CamelContextRegistry;

@RunWith(Arquillian.class)
@ServerSetup({ TransactionEjbTest.JmsQueueSetup.class })
public class TransactionEjbTest {

	@Inject
	private TransactedRouteCaller routeCaller;

    @ArquillianResource
    private CamelContextRegistry contextRegistry;

    private CamelContext camelctx;
	private ConnectionFactory cf;

	@Deployment
	public static JavaArchive createDeployment() throws IOException {
        final JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "tx-ejb.jar");
        archive.addAsManifestResource("spring/tx-ejb-camel-context.xml");
        archive.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
        archive.addPackage(NewTransactionEjb.class.getPackage());
        archive.addPackage(JmsClient.class.getPackage());
        return archive;
	}

    static class JmsQueueSetup implements ServerSetupTask {

        private static String QUEUE_A = "ejbTestRouteQueue";
        private static String QUEUE_B = "ejbTestEjbQueue";

        @Override
        public void setup(ManagementClient managementClient, String containerId) throws Exception {
            JMSUtils.createJmsQueue(QUEUE_A, "java:/jms/queue/" + QUEUE_A, managementClient.getControllerClient());
            JMSUtils.createJmsQueue(QUEUE_B, "java:/jms/queue/" + QUEUE_B, managementClient.getControllerClient());
        }

        @Override
        public void tearDown(ManagementClient managementClient, String containerId) throws Exception {
            JMSUtils.removeJmsQueue(QUEUE_A, managementClient.getControllerClient());
            JMSUtils.removeJmsQueue(QUEUE_B, managementClient.getControllerClient());
        }
    }
    
	@Before
	public void before() throws Exception {
		cf = (ConnectionFactory) new InitialContext().lookup("ConnectionFactory");
		camelctx = contextRegistry.getCamelContext("transaction-ejb-test");
	}

	@Test
	public void singleTransactionTest() throws Exception {
		final String payload = "Message";

		ProducerTemplate producerTemplate = camelctx.createProducerTemplate();
		producerTemplate.sendBody("direct:singleTransactionTest", payload);

		final String routeResponse = JmsClient.receiveFromQueue(cf, "ejbTestRouteQueue", 500);
		final String ejbResponse = JmsClient.receiveFromQueue(cf, "ejbTestEjbQueue", 500);

		Assert.assertEquals(payload, routeResponse);
		Assert.assertEquals("[" + RequiredTransactionEjb.class.getName() + "]:" + payload, ejbResponse);
	}

	@Test
	public void singleTransactionRollbackTest() throws Exception {
		final String payload = "Rollback";

		ProducerTemplate producerTemplate = camelctx.createProducerTemplate();
		producerTemplate.sendBody("direct:singleTransactionRollbackTest", payload);

		final String routeResponse = JmsClient.receiveFromQueue(cf, "ejbTestRouteQueue", 2000);
		final String ejbResponse = JmsClient.receiveFromQueue(cf, "ejbTestEjbQueue", 2000);

		Assert.assertNull(routeResponse); // rollback
		Assert.assertNull(ejbResponse); // rollback
	}

	@Test
	public void newTransactionRollbackTest() throws Exception {
		final String payload = "Rollback";

		ProducerTemplate producerTemplate = camelctx.createProducerTemplate();
		producerTemplate.sendBody("direct:newTransactionRollbackTest", payload);

		final String routeResponse = JmsClient.receiveFromQueue(cf, "ejbTestRouteQueue", 2000);
		final String ejbResponse = JmsClient.receiveFromQueue(cf, "ejbTestEjbQueue", 2000);

		Assert.assertNull(routeResponse); // rollback
		Assert.assertEquals("[" + NewTransactionEjb.class.getName() + "]:" + payload, ejbResponse); // new transaction
	}

	@Test
	public void toRoutePropagatedSingleTransactionTest() throws Exception {
		final String payload = "Message";

		routeCaller.startRouteSingleTransaction(payload);

		final String ejbResponse = JmsClient.receiveFromQueue(cf, "ejbTestEjbQueue", 500);
		final String routeResponse = JmsClient.receiveFromQueue(cf, "ejbTestRouteQueue", 500);

		Assert.assertEquals("[" + TransactedRouteCaller.class.getName() + "]:" + payload, ejbResponse);
		Assert.assertEquals(payload, routeResponse);
	}

	@Test
	public void toRoutePropagatedSingleTransactionRollbackTest() throws Exception {
		final String payload = "Rollback";

		routeCaller.startRouteSingleTransactionRollback(payload);

		final String ejbResponse = JmsClient.receiveFromQueue(cf, "ejbTestEjbQueue", 2000);
		final String routeResponse = JmsClient.receiveFromQueue(cf, "ejbTestRouteQueue", 2000);

		Assert.assertNull(ejbResponse); // rollback
		Assert.assertNull(routeResponse); // rollback
	}

	@Test
	public void toRouteNewTransactionRollbackTest() throws Exception {
		final String payload = "Rollback";

		routeCaller.startRouteNewTransactionRollback(payload);

		final String ejbResponse = JmsClient.receiveFromQueue(cf, "ejbTestEjbQueue", 2000);
		final String routeResponse = JmsClient.receiveFromQueue(cf, "ejbTestRouteQueue", 2000);

		Assert.assertEquals("[" + TransactedRouteCaller.class.getName() + "]:" + payload, ejbResponse);
		Assert.assertNull(routeResponse); // rollback of new transaction
	}
}
