

package gov.va.med.cds.client.functional.vhim400;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import gov.va.med.cds.client.functional.DataGenerator;
import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.junit.runners.SuiteAwareSpringRunner;
import gov.va.med.cds.testharness.AbstractBaseTest;
import gov.va.med.cds.testharness.vhim400.TemplateIdHelper;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Properties;
import java.util.UUID;

import javax.jms.BytesMessage;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TextMessage;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.SessionCallback;
import org.springframework.jndi.JndiTemplate;
import org.springframework.test.context.ContextConfiguration;


@ContextConfiguration( locations = { "classpath:gov/va/med/cds/client/functional/vhim400/asyncClientTestContext.xml",
                "classpath:gov/va/med/cds/client/functional/testDataGeneratorContext.xml" } )
@RunWith( SuiteAwareSpringRunner.class )
public class AsyncCdsClientTest
    extends
        AbstractBaseTest
{

    private static final String TEMPLATE_ID = "TEMPLATE_ID";
    private static final String REQUEST_ID = "REQUEST_ID";
    private static final long RECEIVE_TIMEOUT = 25000L;

    @Autowired
    private JmsTemplate questionnaireResultQueueJmsTemplate;

    @Autowired
    private Destination questionnaireResultsQueue;

    @Autowired
    private Destination cdsDefaultResponseQueueDestination;

    @Autowired
    private DataGenerator dataGenerator = new DataGenerator();
    
    @Autowired
    private JmsTemplate tiuHl7QueueJmsTemplate;
    
    @Autowired
    private JndiTemplate jndiTemplate;
    
    @Autowired
    private Properties jndiEnvironment;

    @Autowired
    private Destination tiuHl7Queue;

    @Autowired
    private Destination fpdsResponseQueueDestination;
    
    @Autowired
    private Destination fpdsAckResponseQueueDestination;
    
    @Autowired
    org.springframework.jms.connection.CachingConnectionFactory cachedFpdsConnectionFactory;
    
    


//    @Test
//    @Suite( groups = "checkintest" )
//    public void testCanCreateBasicDBQCreateTemplateValid( )
//        throws Exception
//    {
//        String dbqCreateTemplate = this.dataGenerator.createQuestionnaireResult( TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID, "JUnit Test - AsyncCdsClientTest" );
//        
//        XmlValidator.validateXml( dbqCreateTemplate, TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID );
//    }
    public void testBasicDbqCreate1AsyncValid( )
        throws Exception
    {

        // create a DBQ create template message
        final String requestId = TemplateIdHelper.getUniqueIdentifier();
        final String dbqCreateTemplate = this.dataGenerator.createQuestionnaireResult( TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID, "JUnit Test - AsyncCdsClientTest" );

        // send the DBQ create XML to the queue.
        Message ack = this.questionnaireResultQueueJmsTemplate.execute( new SessionCallback<Message>()
        {
            @Override
            public Message doInJms( Session session )
                throws JMSException
            {
                // create a temporary queue for the response to be sent to.
                TemporaryQueue replyToQueue = session.createTemporaryQueue();
                
                // create a text message and set the required message properties.
                TextMessage message = session.createTextMessage( dbqCreateTemplate );
                message.setStringProperty( TEMPLATE_ID, TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID );
                message.setStringProperty( REQUEST_ID, requestId );
                message.setJMSReplyTo( replyToQueue );

                MessageProducer producer = session.createProducer( questionnaireResultsQueue );
                producer.send( message );
                MessageConsumer consumer = session.createConsumer( replyToQueue, String.format("%s = '%s'", REQUEST_ID, requestId) );
                Message ack = consumer.receive(RECEIVE_TIMEOUT); 
                consumer.close();
                producer.close();
                return ack;
                
            }
        }, true );

        if ( ack == null )
        {
            fail( String.format( "Receive message timed out. Request ID: %s", requestId ) );
        }

        if ( ack instanceof TextMessage )
        {
            // text the contents of the response to ensure that they are what is expected.
            Document ackDocument = DocumentHelper.parseText( ( ( TextMessage )ack ).getText() );

            // validate that the data in the response is what was expected by the client.
            assertEquals( TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID, ackDocument.selectSingleNode( "//templateId" ).getText() );
            assertEquals( requestId, ackDocument.selectSingleNode( "//requestId" ).getText() );
            assertTrue( ackDocument.selectNodes( "//identifiers/identifier" ).size() == 1 );
        }
        else
        {
            fail( String.format( "Excpected JMS TextMessage acknowledgement. Received acknowledgement of type %s.", ack.getClass().getName() ) );
        }

    }
    
    
    //@Test
    //@Suite( groups = "retired" )
    public void testBasicDbqCreate1AsyncValidDefaultDestination( )
        throws Exception
    {

        // create a DBQ create template message
        final String requestId = TemplateIdHelper.getUniqueIdentifier();
        final String dbqCreateTemplate = this.dataGenerator.createQuestionnaireResult( TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID, "JUnit Test - AsyncCdsClientTest" );

        // send the DBQ create XML to the queue.
        Message ack = this.questionnaireResultQueueJmsTemplate.execute( new SessionCallback<Message>()
        {
            @Override
            public Message doInJms( Session session )
                throws JMSException
            {
                // create a text message and set the required message properties.
                TextMessage message = session.createTextMessage( dbqCreateTemplate );
                message.setStringProperty( TEMPLATE_ID, TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID );
                message.setStringProperty( REQUEST_ID, requestId );

                MessageProducer producer = session.createProducer( questionnaireResultsQueue );
                producer.send( message );
                MessageConsumer consumer = session.createConsumer( cdsDefaultResponseQueueDestination, String.format("%s = '%s'", REQUEST_ID, requestId) );
                Message ack = consumer.receive(RECEIVE_TIMEOUT); 
                consumer.close();
                producer.close();
                return ack;
                
            }
        }, true );

        if ( ack == null )
        {
            fail( String.format( "Receive message timed out. Request ID: %s", requestId ) );
        }

        if ( ack instanceof TextMessage )
        {
            // text the contents of the response to ensure that they are what is expected.
            Document ackDocument = DocumentHelper.parseText( ( ( TextMessage )ack ).getText() );

            // validate that the data in the response is what was expected by the client.
            assertEquals( TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID, ackDocument.selectSingleNode( "//templateId" ).getText() );
            assertEquals( requestId, ackDocument.selectSingleNode( "//requestId" ).getText() );
            assertTrue( ackDocument.selectNodes( "//identifiers/identifier" ).size() == 1 );
        }
        else
        {
            fail( String.format( "Excpected JMS TextMessage acknowledgement. Received acknowledgement of type %s.", ack.getClass().getName() ) );
        }

    }


    //@Test
   // @Suite( groups = "retired" )
    public void testBasicDbqCreateAsyncInvalidMessageFormat( )
        throws Exception
    {

        // create a DBQ create template message
        final String requestId = TemplateIdHelper.getUniqueIdentifier();
        final String dbqCreateTemplate = this.dataGenerator.createQuestionnaireResult( TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID, "JUnit Test - AsyncCdsClientTest" );

        // remove something from the message that is required.
        Document dbqCreateDoc = DocumentHelper.parseText( dbqCreateTemplate );
        dbqCreateDoc.selectSingleNode( "//detailXML" ).detach();
        final String dbqCreateTemplateInvalid = dbqCreateDoc.asXML();

        assertTrue( !dbqCreateTemplate.equals( dbqCreateTemplateInvalid ) );

        // send the DBQ create XML to the queue.
        Message ack = this.questionnaireResultQueueJmsTemplate.execute( new SessionCallback<Message>()
        {
            @Override
            public Message doInJms( Session session )
                throws JMSException
            {
                // create a temporary queue for the response to be sent to.
                TemporaryQueue replyToQueue = session.createTemporaryQueue();
                
                // create a text message and set the required message properties.
                TextMessage message = session.createTextMessage( dbqCreateTemplateInvalid );
                message.setStringProperty( TEMPLATE_ID, TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID );
                message.setStringProperty( REQUEST_ID, requestId );
                message.setJMSReplyTo( replyToQueue );

                MessageProducer producer = session.createProducer( questionnaireResultsQueue );
                producer.send( message );
                MessageConsumer consumer = session.createConsumer( replyToQueue );
                Message ack = consumer.receive(RECEIVE_TIMEOUT); 
                consumer.close();
                producer.close();
                return ack;
                
            }
        }, true );

        if ( ack == null )
        {
            fail( String.format( "Receive message timed out. Request ID: %s", requestId ) );
        }

        if ( ack instanceof TextMessage )
        {
            // text the contents of the response to ensure that they are what is expected.
            Document ackDocument = DocumentHelper.parseText( ( ( TextMessage )ack ).getText() );

            // validate that the data in the response is what was expected by the client.
            assertEquals( TemplateIdHelper.DBQ_CREATE1_TEMPLATE_ID, ackDocument.selectSingleNode( "//templateId" ).getText() );
            assertEquals( requestId, ackDocument.selectSingleNode( "//requestId" ).getText() );
            assertTrue( ackDocument.selectNodes( "//identifiers/identifier" ).size() == 0 );
            assertTrue( ackDocument.selectNodes( "//errorSection/fatalErrors/fatalError" ).size() == 1 );
        }
        else
        {
            fail( String.format( "Excpected JMS TextMessage acknowledgement. Received acknowledgement of type %s.", ack.getClass().getName() ) );
        }

    }
    
    @Test
    @Suite( groups = "prototype" )
    public void testFPDSCreateHl7VistaTIU( )
        throws Exception
    {
    	//NOTE: THIS IS A MANUAL SMOKETEST - probably not safe to make part of the build with asserts, etc. as latency, etc. can cause it to fail - would need a blocking connection, etc.
    	
    	// send the Hl7 to the queue.
        Message ack = this.tiuHl7QueueJmsTemplate.execute( new SessionCallback<Message>()
        {
        	
        	@Override
            public Message doInJms( Session session )
                throws JMSException
            {
        		String messageControlId= (UUID.randomUUID().toString()).trim();	
        		String timestamp = new SimpleDateFormat("yyyyMMddHHmmssZ").format(Calendar.getInstance().getTime());
        		
        		//TCPMONITOR_DEV3 final String tiuNote = "MSH^~|\\&^HDRFTP^200HD~HDR.MED.DNS   ~DNS^TIUHL7^654~IP         ~DNS^20151216094505-0500^^MDM~T02^10101117010^T^2.4^^^AL^AL^USA^^^^\r\nEVN^T02^20051022154503-0500^^2^32885~Blankenship~George~~~~~~USVHA&&0363~L~~~NI~FACILITY&STATION&L^20151216094505-0500-0500^\r\nPID^1^^~~~USVHA~NI~VA FACILITY ID&200M&L|1~~~USVHA~PI~VA FACILITY&552&L|666223456~~~USSSA~SS~VA FACILITY ID&STATION&L^^Zzzretfivefiftyone~Patient~~~~~L^Government~~~~~~M^19350407^m^^w^1335 East West Highway~~Silver Sprung~MD~20910^^(301) 734-0400^(301) 734-0227^^M^C^^^^^American^^^^^veteran^^^N^\r\nPV1^^^CARDIOLOGY~~~~~C^^123456789~~~USVHA~~DNS    &552&L~~^^^^^^^^^^^^^^NEW^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\nTXA^^PR^TEXT^20151210094503-0500^^20151216094505-0500^^^988~Provider~Five~~~~~~~~~~~facility&station&L^988~Provider~Five~~~~~~~~~~~facility&station&L^^123456789~USVHA^^^220051022154424~HT^ASI-ADDICTION SEVERITY INDEX^PA^U^^AC^end of collection period^^\r\nOBX^1^TX^^^This is a Note for MJT development - note 7010  ^^^^^^^^^^^^^^";
        		//final String tiuNote = "MSH^~|\\&^HDRFTP^200HD~HDR.MED.DNS   ~DNS^TIUHL7^654~islvhdvsta01.DNS   .med.DNS   ~DNS^20151217094505-0500^^MDM~T02^"+messageControlId+"^T^2.4^^^AL^AL^USA^^^^\r\nEVN^T02^20151217094505-0500^^2^32885~Blankenship~George~~~~~~USVHA&&0363~L~~~NI~FACILITY&STATION&L^20151217094505-0500^\r\nPID^1^^~~~USVHA~NI~VA FACILITY ID&200M&L|1~~~USVHA~PI~VA FACILITY&552&L|666223456~~~USSSA~SS~VA FACILITY ID&STATION&L^^Zzzretfivefiftyone~Patient~~~~~L^Government~~~~~~M^19350407^m^^w^1335 East West Highway~~Silver Sprung~MD~20910^^(301) 734-0400^(301) 734-0227^^M^C^^^^^American^^^^^veteran^^^N^\r\nPV1^^^CARDIOLOGY~~~~~C^^123456789~~~USVHA~~DNS    &552&L~~^^^^^^^^^^^^^^NEW^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\nTXA^^PR^TEXT^20151217094505-0500^^20151217094505-0500^^^988~Provider~Five~~~~~~~~~~~facility&station&L^988~Provider~Five~~~~~~~~~~~facility&station&L^^123456789~USVHA^^^220051022154424~HT^ASI-ADDICTION SEVERITY INDEX^PA^U^^AC^end of collection period^^\r\nOBX^1^TX^^^This is a Note for MJT development - note "+messageControlId+"  ^^^^^^^^^^^^^^";
        		final String tiuNote = "MSH^~|\\&^HDRFTP^200HD~HDR.MED.DNS   ~DNS^TIUHL7^654~islvhdvsta01.DNS   .med.DNS   ~DNS^"+timestamp+"^^MDM~T02^"+messageControlId+"^T^2.4^^^AL^AL^USA^^^^\r\nEVN^T02^"+timestamp+"^^2^32885~Blankenship~George~~~~~~USVHA&&0363~L~~~NI~FACILITY&STATION&L^"+timestamp+"^\r\nPID^1^^~~~USVHA~NI~VA FACILITY ID&200M&L|1~~~USVHA~PI~VA FACILITY&552&L|666223456~~~USSSA~SS~VA FACILITY ID&STATION&L^^Zzzretfivefiftyone~Patient~~~~~L^Government~~~~~~M^19350407^m^^w^1335 East West Highway~~Silver Sprung~MD~20910^^(301) 734-0400^(301) 734-0227^^M^C^^^^^American^^^^^veteran^^^N^\r\nPV1^^^CARDIOLOGY~~~~~C^^123456789~~~USVHA~~DNS    &552&L~~^^^^^^^^^^^^^^NEW^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\nTXA^^PR^TEXT^"+timestamp+"^^"+timestamp+"^^^988~Provider~Five~~~~~~~~~~~facility&station&L^988~Provider~Five~~~~~~~~~~~facility&station&L^^123456789~USVHA^^^220051022154424~HT^ASI-ADDICTION SEVERITY INDEX^PA^U^^AC^end of collection period^^\r\nOBX^1^TX^^^This is a Note for MJT development - note from AsyncCdsClientTest: "+messageControlId+"  ^^^^^^^^^^^^^^";
        		
        		System.out.println("\n");
        		System.out.println(tiuNote);
        		System.out.println("\n\n");
        		
                BytesMessage message = session.createBytesMessage();
                // encode the HL7 message
                Charset charset = Charset.forName( "ISO-8859-1" );
                ByteBuffer buffer = charset.encode( tiuNote );

                message.writeBytes( buffer.array() );
               
                MessageProducer producer = session.createProducer( tiuHl7Queue );
                producer.send( message );
              
                MessageConsumer consumer = session.createConsumer( fpdsAckResponseQueueDestination ); 
                //Message ack = consumer.receive(120000); 
                Message ack = consumer.receive(20000); 
                consumer.close();
                producer.close();
                return ack;
                
            }
        }, true );
 
        /*if ( ack instanceof TextMessage )
        {
           String message = ((TextMessage) ack).getText();
           System.out.println("ACK RESPONSE: " + message);

        }*/
        if ( ack instanceof BytesMessage )
        {
            BytesMessage bytesMsg = ( BytesMessage )ack;
            int msgLength = ( int )bytesMsg.getBodyLength();
            byte[] messageBytes = new byte[msgLength];
            bytesMsg.readBytes( messageBytes );
            ByteBuffer messageBuffer = ByteBuffer.wrap( messageBytes );
            String hl7Payload = new String( messageBuffer.array() );
            System.out.println("ACK RESPONSE: " + hl7Payload);
        }
               

    }

}