/**
 * 
 */


package gov.va.med.cds.hapi;


import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.exceptionframework.ExceptionInfo;
import gov.va.med.cds.exceptionframework.ExceptionUtil;


/**
 * @author vhaislegberb
 *
 */
public abstract class AbstractHl7Support
    implements
        HL7SupportInterface
{

    private static final String APPLICATION_NAME = "HL7Support";

    private static Log logger = LogFactory.getLog( AbstractHl7Support.class );

    protected static final String T_SENDING_APPLICATION = "/MSH-3";
    
    protected static final String T_FULL_SENDING_FACILITY = "/MSH-4";
    
    protected static final String T_STATION_NUMBER = "/MSH-4-1";
    
    protected static final String T_SENDING_FACILITY = "/MSH-4-2";
    
    protected static final String T_SENDING_FACILITY_TYPE = "/MSH-4-3";
    
    protected static final String T_RECEIVING_APPLICATION = "/MSH-5";
    
    protected static final String T_RECEIVING_FACILITY_STATION = "/MSH-6";

    protected static final String T_RECEIVING_FACILITY = "/MSH-6-2";
    
    protected static final String T_RECEIVING_FACILITY_TYPE = "/MSH-6-3";
    
    protected static final String T_MESSAGE_DATE_TIME = "/MSH-7";
    
    protected static final String T_MESSAGE_CONTROL_ID = "/MSH-10";

    protected static final String T_APPLICATION_ACKNOWLEDGEMENT = "/MSH-16";
    
    protected static final String T_ACKNOWLEDGEMENT_CODE = "/MSA-1";
    
    protected static final String T_ACK_ORIGINAL_MSG_ID = "/MSA-2";

    protected static final String T_SPQ_Q08_WHAT_CODE = "/SPR-4-2-8";
    
    protected static final String T_SPQ_Q04_USER_ID = "/SPR-4-2-4";

    protected static final String T_QRY_Q01_WHAT_CODE = "/QPD-4";

    
    public enum AppAcknowledge
    {
        AL, ER, SU, NE
    };


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.hapi.HL7SupportInterface#createMessageDateTime()
     */
    @Override
    public String createMessageDateTime( )
    {

        return HL7Utility.createMessageDateTime();
    }


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.hapi.HL7SupportInterface#createMessageControlId()
     */
    @Override
    public String createMessageControlId( )
    {
        return HL7Utility.createMessageControlId();
    }

    public String createHl7WriteAckReject(String rejectMessage )
    {
        return createHl7WriteAckReject(getReceivingApplication(), getFullReceivingFacility(), getSendingApplication(), getFullSendingFacility(), getMessageDateTime(), createMessageControlId(), getMessageControlId(), rejectMessage );
    	
    }
    
    public String createHl7WriteAckReject(String receivingApp, String receivingFacility, String sendingApp, String sendingFacility, String messageDate, String messageControlId, String originalMessageControlId, String rejectMessage )
    {
        return String.format( "MSH^~|\\&^%s^%s^%s^%s^%s^^ACK^%s^T^2.4\rMSA^CR^%s^%s",receivingApp,receivingFacility, sendingApp, sendingFacility, messageDate, messageControlId, originalMessageControlId, rejectMessage  );
    }

    /*
     * (non-Javadoc)
     * 
     * Generates ack response message sending-application value modified in case of HTH
     */
    public String createAckResponse( )
    {
        return String.format( "MSH^~|\\&^%s^200HD~%s~DNS^%s^%s~%s~DNS^%s^^ACK^%s^P^2.4^^^AL^NE\rMSA^", getReceivingApplication(),
                        getReceivingFacility(), getMessageData( T_SENDING_APPLICATION ), getStationNumber(), getSendingFacility(), createMessageDateTime(),
                        createMessageControlId() );
    }
    
    private String createAppAckWithErrorResponse( String errorsInRepsonse, String exceptions )
    {

        return String.format( createAckResponse().concat( "AR^%s\rERR^%s^%s" ), getMessageControlId(), errorsInRepsonse, exceptions );

    }


    private String createAppAckNoErrors( List<String> ignoreAppAckSendingApps )
    {
        String appAck = (null != ignoreAppAckSendingApps && ignoreAppAckSendingApps.contains(getSendingApplication())) ? 
                        null : String.format( createAckResponse().concat( "AA^%s" ), getMessageControlId() );

        return appAck;
    }


    private boolean requiresAcknowledgment( String appAck )
    {
        AppAcknowledge aa = AppAcknowledge.valueOf( appAck );

        return ( aa.equals( AppAcknowledge.AL ) || aa.equals( AppAcknowledge.ER ) || aa.equals( AppAcknowledge.SU ) );
    }


    /*
     * (non-Javadoc)
     * @see gov.va.med.cds.hapi.HL7SupportInterface#getApplicationAcknowledgement(java.util.List, java.lang.String)
     */
    @Override
    public String getApplicationAcknowledgement( List<Exception> exceptionList, String vhimResponse, List<String> ignoreAppAckSendingApps )
    {
        String errorsInVhimResponse = null;
        String applicationAckMessage = null;

        String appAck = getApplicationAcknowledgementType();

        try
        {
            errorsInVhimResponse = errorSectionExistsInResponse( vhimResponse );
        }
        catch ( HL7SupportException e )
        {
            ExceptionInfo exceptionInfo = new ExceptionInfo();
            exceptionInfo.setException( e );
            exceptionInfo.setClientMessage( e.getErrorCode().getClientMessage() );

            ExceptionUtil.logException( exceptionInfo, logger, e.getMessage(), null, null, APPLICATION_NAME );

            errorsInVhimResponse = e.getErrorCode().getClientMessage();
        }

        String exceptions = getExceptions( exceptionList );
        
        // check if vhimResponse has an error WRITE_REQUEST_CENSUS_ACK_ALREADY_SENT, then do not send AR again
        if ( StringUtils.containsIgnoreCase( vhimResponse, "WRITE_REQUEST_CENSUS_ACK_ALREADY_SENT" ) )
        {
            return null;
        }

        /**
         * Application acknowledgement is created in the following scenarios 1.ACKNOWLEDGMENT TYPE = AL 2.ACKNOWLEDGMENT
         * TYPE = ER and Errors exists in CDS response 3.ACKNOWLEDGMENT TYPE = SU and No errors exists in CDS response
         */
        if ( requiresAcknowledgment( appAck ) )
        {

            if ( appAck.equalsIgnoreCase( "ER" ) && errorsInVhimResponse.length() != 0 )
            {
                applicationAckMessage = createAppAckWithErrorResponse( errorsInVhimResponse, exceptions );
            }
            else if ( appAck.equalsIgnoreCase( "SU" ) && errorsInVhimResponse.length() == 0 )
            {
                applicationAckMessage = createAppAckNoErrors( ignoreAppAckSendingApps );
            }
            else if ( appAck.equalsIgnoreCase( "AL" ) )
            {
                if ( errorsInVhimResponse.length() != 0 )
                {
                    if ( errorsInVhimResponse.contains( "ORA-00001: unique constraint" ) )
                    {
                        logger.error( "MessageMediator : Duplicate HL7 write request message sent by HomeTelehealth system." );
                        applicationAckMessage = createAppAckNoErrors( ignoreAppAckSendingApps );
                    }
                    else
                    {
                        applicationAckMessage = createAppAckWithErrorResponse( errorsInVhimResponse, exceptions );
                    }
                }
                else
                {
                    applicationAckMessage = createAppAckNoErrors( ignoreAppAckSendingApps );
                }
            }
        }

        return applicationAckMessage;
    }


    /**
     * Checks to see if there are any errors that have been reported in the response.
     * @param response The response to check for errors in.
     * @return The errors in the response.
     */
    private String errorSectionExistsInResponse( String response )
    {

        String errorsInVhimResponse = "";

        if ( response == null )
        {
            throw new HL7SupportException( ErrorCodeEnum.WRITE_RESPONSE_NULL );
        }

        try
        {
            Document document = DocumentHelper.parseText( response );

            Element rootElement = document.getRootElement();

            // iterate through child elements of root
            for ( Iterator<?> rootIterator = rootElement.elementIterator( "errorSection" ); rootIterator.hasNext(); )
            {
                Element childElement = ( Element )rootIterator.next();

                Iterator<?> childIterator = childElement.elementIterator();

                while ( childIterator.hasNext() )
                {
                    Element subElement = ( Element )childIterator.next();
                    if ( subElement.getName().equalsIgnoreCase( "fatalErrors" ) || subElement.getName().equalsIgnoreCase( "errors" )
                                    || subElement.getName().equalsIgnoreCase( "warnings" ) )
                    {

                        errorsInVhimResponse = errorsInVhimResponse + subElement.getStringValue();
                    }
                }
            }
        }
        catch ( DocumentException e )
        {
            throw new HL7SupportException( ErrorCodeEnum.WRITE_RESPONSE_MALFORMED, e );
        }

        return errorsInVhimResponse;
    }


    private String getExceptions( List<Exception> exceptionList )
    {

        String exceptions = "";
        for ( Exception exception : exceptionList )
        {

            exceptions = exceptions.concat( exception.getMessage() );
        }

        return exceptions;
    }
    
    
    protected String processingHTHLogic( String aSendingApplication )
    {
        // Do the specialized logic for Home Tele-Health vitals data.
    	
        String htSendingApplication = aSendingApplication ;
    	String receivingApplication = getReceivingApplication();
        
    	if ( "HDRVTLS".equals( htSendingApplication ) )
        {
            if ( "HTH HDR".equals( receivingApplication ) )
            {
                htSendingApplication += "-HTH";
            }
        }

        // Do the specialized logic for Home Tele-Health survey data.
        if ( "HTAPPL".equals( htSendingApplication ) )
        {
            if ( "HDR HTPS".equals( receivingApplication ) ) 
            {
            	String surveyVersion = getSurveyVersion();
            	htSendingApplication += "-PS";
            	htSendingApplication += surveyVersion;
            }
            else if ( "HDR HTADL".equals( receivingApplication ) )
            {
                htSendingApplication += "-ADL";
            }
            else if ( "HDR HTDMP".equals( receivingApplication ) )
            {
                htSendingApplication += "-DMP";
            }
            else if ( "HT CENSUS".equals( receivingApplication ) )
            {
                htSendingApplication += "-CENSUS";
            }
        }
        
        return htSendingApplication;
    }
    
    
    abstract public String getReceivingApplication();
   
}
