/********************************************************************
 * Copyright © 2004 EDS. All rights reserved
 ********************************************************************/
package gov.va.med.esr.messaging.service.inbound;

// Framework classes
import java.io.Serializable;

import gov.va.med.fw.hl7.HL7MessageUtils;
import gov.va.med.fw.hl7.InvalidMessageException;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.builder.BuilderException;

import gov.va.med.esr.common.model.ee.VerificationInfo;
import gov.va.med.esr.common.model.lookup.MessageType;
import gov.va.med.esr.common.model.messaging.MessageLogEntry;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.PersonLockedReason;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.VPIDEntityKeyImpl;
import gov.va.med.esr.common.model.workload.WorkflowCaseInfo;
import gov.va.med.esr.messaging.constants.HL7Constants;
import gov.va.med.esr.messaging.util.Z11NonCatastrophicException;
import gov.va.med.esr.service.ScheduledTaskService;

/**
 * A service to process inbound Z11 message from MVR.
 * 
 * @author Vu Le
 * @version 1.0
 */
public class Z11ProcessService extends AbstractInboundProcessService {

    private ScheduledTaskService    scheduledTaskService;

	/**
	 * Default constructor.
	 */
	public Z11ProcessService() {
		super();
	}

	/**
	 * @see gov.va.med.esr.messaging.service.inbound.AbstractInboundProcessService#doProcessMessage(gov.va.med.esr.common.model.person.Person, gov.va.med.fw.hl7.Message, java.lang.String)
	 */
	protected final MessageLogEntry doProcessMessage( Person person,
																	  Message message, 
																	  String formattedBody )
		throws InboundProcessException {
		
		if (person == null) {
			throw super.createInboundProcessException( message, 
																	 formattedBody,
																	 null, 
																	 "AE: Could not find the person", 
																	 null );
		}
		
		try {
			return this.doProcessMessage( person, 
													message, 
													formattedBody, 
													super.getReferencedMessageLogEntry(message) );
		} 
		catch (InboundProcessException e) {
			throw super.createInboundProcessException( message, 
																	 formattedBody,
																	 person, 
																	 e.getMessage(), 
																	 e );
		} 
		catch (ServiceException e) {
			throw super.createInboundProcessException( message, 
																	 formattedBody,
																	 person, 
																	 e.getMessage(), 
																	 e );
		}
	}

	/**
	 * Builds a person from an inbound Z11 message then calls appropriate
	 * business service to process an incoming person.  If there is not 
	 * any exception, create a message log entry to be persisted.  Otherwise
	 * create an InboundProcessException to trigger a roll back of persisted
	 * data.  In this service, an around advice is configured for a Z11 message
	 * parser to process specific exceptions surfaced during a message building 
	 * process.  If the exceptions are deemed to be non catastrophic, a 
	 * Z11NonCatastrophicException is thrown.  Otherwise, BuilderException and 
	 * ServiceException are thrown to allow persisted data to be roll-backed. 
	 * 
	 * This new feature is added for defect 2256, and 2258 - VL 
	 * 
	 * @param person An incoming person
	 * @param message A incoming message
	 * @param formattedBody An incoming message's XML formatted body
	 * @param initiatingMessage A raw message's data
	 * @return MessageLogEntry An entity that ties an incoming message and a person
	 * @throws InboundProcessException In case of error to be roll-backed
	 */
	private MessageLogEntry doProcessMessage( Person person, 
															Message message,
															String formattedBody, 
															MessageLogEntry initiatingMessage )
		throws InboundProcessException {
		
		MessageLogEntry mle = initiatingMessage;
		
		try {
			// calls a Z11Parser to build a person from an inbound Z11 message 
			person = super.buildPerson(person, message);
			
			// Process an incoming message
			person = getMessagingService().processNonQueryZ11( person, createVerificationInfo(person.getPersonEntityKey(), message, initiatingMessage) );

			// Create a message log entry for the processed person
			mle = super.createMessageLog(message, formattedBody, person, initiatingMessage);
		} 
		catch (BuilderException e) {
			throw super.createInboundProcessException( message, 
																	 formattedBody,
																	 person, 
																	 e.getMessage(), 
																	 e, 
																	 initiatingMessage );
		}
		catch( Z11NonCatastrophicException e ) {
			try {
				// Create a message log entry for the processed person
				mle = super.createMessageLog(message, formattedBody, person, initiatingMessage);
			}
			catch( ServiceException serviceException ) {
				throw super.createInboundProcessException( message, 
																		 formattedBody,
																		 person, 
																		 serviceException.getMessage(), 
																		 serviceException, 
																		 initiatingMessage );
				
			}
		}
		catch (ServiceException e) {
			throw super.createInboundProcessException( message, 
																	 formattedBody,
																	 person, 
																	 e.getMessage(), 
																	 e, 
																	 initiatingMessage );
		}
		return mle;
	}
	
	public VerificationInfo createVerificationInfo(PersonEntityKey personKey, Message message, MessageLogEntry referencedMessage) throws ServiceException {
		try {
			Serializable personId = personKey != null ? personKey.getKeyValue() : null;
			if(referencedMessage == null) {
				referencedMessage = getReferencedMessageLogEntry(message);
         }
			if(personId == null && referencedMessage != null) {
				personId = referencedMessage.getPersonId();
         }
         
			// Custom non-BOM object necessary for passing MVR-specific VBA data to rules
         // Entitlement Code 
         // Message Type Code: ORUZ11 or ORFZ11
         // Unsolicited Type: VBA Push ORUZ11 and HINQ ORUZ11.
         // Error Text : "No data on file"
			VerificationInfo info = new VerificationInfo( HL7MessageUtils.getEntitlementCode( message ), 
                                                       getMessageTypeCode(),
                                                       HL7MessageUtils.getErrorText( message ) );
			
			info.setCombinedSCPercentage( HL7MessageUtils.getCombinedPercentage( message ) );
         info.setUnsolicitedType( HL7MessageUtils.getUnsolicitedType( message ) );
         
			// create workflow case object for potential usage by Rules
         WorkflowCaseInfo caseInfo = new WorkflowCaseInfo();
         caseInfo.setTransmissionSite(getLookupService().getVaFacilityByStationNumber(message.getSendingFacility()));
         caseInfo.setMessageID(message.getMessageID());
         
			info.setWorkflowCaseInfo( caseInfo );
         
			return info;
		} 
      catch(InvalidMessageException e) {
			throw new ServiceException("Unable to create VerificationInfo object", e);
		}
	}
	
	// CCR11575 -- for process add a person, if the inbound Z11 is rejected due to pending traits updates
	// then start a clock and wait to re-query VBA
   protected MessageLogEntry processPendingTraits(Person person, Message message) throws Exception 
   {
		if (HL7Constants.ORFZ11.equals(message.getType()))
		{	
			this.getScheduledTaskService().startZ11PendingTraitsClock(person);
			InboundProcessException e = this.createInboundProcessException(message, null, person,
			        "AE: Ignoring inbound message.  Target Person is locked because of reason '"
			          + person.getPersonLockedReason()
			          + "'. Starting clock to resend QRY~Z11.", null);
			
			// Create a message log entry for the processed person
			MessageLogEntry mle = super.createMessageLog(message, null, person, e.getMessage(), e, super.getReferencedMessageLogEntry(message));
			return mle;
		} else {
			return super.processPendingTraits(person, message);
		}
	}
	
	/**
     * @return Returns the scheduledTaskService.
     */
    public ScheduledTaskService getScheduledTaskService() {
        return scheduledTaskService;
    }

    /**
     * @param scheduledTaskService The scheduledTaskService to set.
     */
    public void setScheduledTaskService(ScheduledTaskService scheduledTaskService) {
        this.scheduledTaskService = scheduledTaskService;
    }

	}