package gov.va.med.esr.messaging.service.inbound;


import gov.va.med.esr.service.MilitaryInfoService;
import gov.va.med.esr.service.MsdsResponseInfo;
import gov.va.med.esr.jms.vadir.outboundResponse.VadirServiceResponseWrapper;
import gov.va.med.esr.common.builder.msds.MilitaryServiceBuilderForMSDS;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.log4j.Logger;

import java.math.BigDecimal;
import gov.va.med.esr.common.model.CommonEntityKeyFactory;
import gov.va.med.esr.common.model.lookup.MessageStatus;
import gov.va.med.esr.common.model.messaging.MsdsErrorLogEntry;
import gov.va.med.esr.common.model.messaging.MsdsMessageLogEntry;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.service.LogTransmissionService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.messaging.service.MessageProcessServiceUtil;
import gov.va.med.fw.service.ServiceException;



public class MsdsInboundProcessService  {
	
	private static final Logger logger = Logger.getLogger(MsdsInboundProcessService.class);
	private LogTransmissionService logMessagingService;

	private PersonService personService;

	private LookupService lookupService;
	private MilitaryInfoService militaryInfoService;
	private MilitaryServiceBuilderForMSDS militaryServiceBuilderForMSDS;
	

	public VadirServiceResponseWrapper processMessage(VadirServiceResponseWrapper message) {
		Validate.notNull(message, "Message cannot be null");
        if (logger.isDebugEnabled()) {
         	logger.debug("MsdsInboundProcessService processMessage : message: " + ToStringBuilder.reflectionToString(message, ToStringStyle.MULTI_LINE_STYLE) );
 	    }
		MsdsErrorLogEntry errorLogEntry = null;
		MsdsMessageLogEntry referencedLogEntry = null;
		
		try {
			// get referenced MessageLogEntry for this solicited message and
			// update it
			referencedLogEntry = getReferencedMessageLogEntry(message);

			if (referencedLogEntry == null) {				
				logger.error("Creating an error log message for reference log entry id: " + message.getRequestId() + "person id:" + message.getPersonId());				
				errorLogEntry = createErrorMessageLogEntry(message, null);

			} else {					
				if(logAcknowledgment(message, referencedLogEntry)){
					logger.info("Calling Process MSDSData");
					MsdsResponseInfo info = (MsdsResponseInfo)this.getMilitaryServiceBuilderForMSDS().build(message);
					this.getMilitaryInfoService().processMSDSData(info);
					logger.info("Completed Calling Process MSDSData");					
				}
				
			}

		} catch (InboundProcessException e) {
			logger.error("Exception processing Solicitated MSDS message ", e);
			errorLogEntry = e.getMsdsErrorLogEntry();
		} catch (Throwable ex) {
			try {
				logger.error("Throwable " + ex);
				errorLogEntry = this.createErrorMessageLogEntry(message, ex);
			} catch (ServiceException e) {
				// Could neither process the message nor create a MessageLogEntry
				// object to persist into the transaction log.
				// Dump the message data for troubleshooting.
				logger.error("Could not process Solicited MSDS message nor log "
						+ "into transaction log: " + message.getRequestId(), e);
			}
		}
		if (errorLogEntry != null) {
			if(referencedLogEntry != null){
				logger.error("updating the error messaging and calling log message again");
				referencedLogEntry.setMsdsError(errorLogEntry);
				logMessage(referencedLogEntry, message);				
			}
			else{
				logger.error("Error Log is being created for incoming response message: " + message.getRequestId());
				logErrorMessage(errorLogEntry);				
			}
			
		} else {
			//Logging payload
			logMessagePayload(referencedLogEntry, message);
		}
		
		return message;

	}	
	

	/**
	 * Method to save the acknowledgment information in a message log.
	 * 
	 * @param message
	 * @return The message log.
	 * @throws ServiceException
	 */
	protected final boolean logAcknowledgment(
			VadirServiceResponseWrapper message,
			MsdsMessageLogEntry initiatingMessage) {

		boolean continueProcessing = true;

        if (message != null) {
            if (message.getResponseType() == 1) {
                  if (message.getErrorMessage() != null) {
                        if (message.getErrorMessage().equalsIgnoreCase(
                                    "NoPersonFound")
                                    || message.getErrorMessage().equalsIgnoreCase(
                                                "MultiplePeopleFoundinBIRLS")
                                    || message.getErrorMessage().equalsIgnoreCase(
                                                "MultiplePeopleFoundinVADIR")) {
                              initiatingMessage
                                          .setStatus(getMessageStatus(MessageStatus.COMPLETE));
                              logger.info("Received known error message from broker, sending for processing: " + message.getErrorMessage()+"," + "Person id: "+ message.getPersonId() + ", request id:"+ message.getRequestId());
                        } else {
                              initiatingMessage
                                          .setStatus(getMessageStatus(MessageStatus.RETRANSMISSION_FAILED));
                              MsdsErrorLogEntry error = new MsdsErrorLogEntry();
                              error.setErrorText(message.getErrorMessage());
                              initiatingMessage.setMsdsError(error);
                              continueProcessing = false;
                              logger.info("Received UNKNOWN error message from broker " + message.getErrorMessage()+"," + "Person id: "+ message.getPersonId() + ", request id:"+ message.getRequestId());
                        }
                        
                  } else {
                        initiatingMessage
                                    .setStatus(getMessageStatus(MessageStatus.RETRANSMISSION_FAILED));
                        MsdsErrorLogEntry error = new MsdsErrorLogEntry();
                        error.setErrorText("The incoming error message is null");
                        initiatingMessage.setMsdsError(error);
                        continueProcessing = false;
                        logger.info("Received message of type error but error message is null: " +"," + "Person id: "+ message.getPersonId() + ", request id:"+ message.getRequestId());
                        
                  }

            }
            else {                  
                  initiatingMessage
                              .setStatus(getMessageStatus(MessageStatus.COMPLETE));
                  logger.info("Successful Message From Broker: Should have received military service history object" );
            }
      }
      else{
            initiatingMessage
            .setStatus(getMessageStatus(MessageStatus.RETRANSMISSION_FAILED));
            MsdsErrorLogEntry error = new MsdsErrorLogEntry();
            error.setErrorText("The incoming message is null");
            initiatingMessage.setMsdsError(error);
            continueProcessing = false;
            logger.info("Message From Broker was null " );
      }
      logMessage(initiatingMessage, message);
      return continueProcessing;
}

	

	protected final MessageStatus getMessageStatus(MessageStatus.Code code) {
		try {
			return (MessageStatus) this.lookupService.getByCode(
					MessageStatus.class, code);
		} catch (ServiceException e) {
			// ASSERT: Should not happen if the database is loaded.
			throw new RuntimeException("Could not load the message status: "
					+ code, e);
		}
	}

	/**
	 * Returns the referenced MessageLogEntry
	 * 
	 * @param message
	 * @return
	 * @throws ServiceException
	 */
	protected final MsdsMessageLogEntry getReferencedMessageLogEntry(
			VadirServiceResponseWrapper message) throws ServiceException {

		BigDecimal referencedLogId = new BigDecimal(message.getRequestId());
		return referencedLogId == null ? null
				: getMessageLogEntry(referencedLogId);

	}

	protected MsdsErrorLogEntry createErrorMessageLogEntry(
			VadirServiceResponseWrapper message, Throwable e)
			throws ServiceException {

		MsdsErrorLogEntry errorLogEntry = null;
		
		if(e != null){			
			errorLogEntry = MessageProcessServiceUtil.createMsdsErrorLog(
					MessageProcessServiceUtil.formatMSDSErrorText(e.getMessage() + "- MESSAGE  ID: "+ message.getRequestId()),
					MessageProcessServiceUtil.formatInternalMSDSErrorText(e));
			
			
		}
		else{
			errorLogEntry = MessageProcessServiceUtil.createMsdsErrorLog(
					"MESSAGE  ID: "+ message.getRequestId()+ "Could Not Be Associated with Request",
					"");
			
		}

		

		return errorLogEntry;

	}

	private Person findPerson(BigDecimal personId, BigDecimal referenceId)
			throws ServiceException {
		Person person = null;
		person = getPersonService().getPerson(
				CommonEntityKeyFactory.createPersonIdEntityKey(personId
						.toString()));
		if (person == null) {

			throw new ServiceException(
					"Unable to retrieve Person for Broker Message ID: "
							+ referenceId);
		}
		return person;
	}

	protected final MsdsMessageLogEntry getMessageLogEntry(
			BigDecimal messageControlIdentifier) throws ServiceException {
		return this.logMessagingService
				.getMessageLogEntry(messageControlIdentifier);
	}

	/**
	 * Method to log the message.
	 * 
	 * @param logEntry
	 *           The message log entry.
	 */
	protected final void logMessage(MsdsMessageLogEntry logEntry, VadirServiceResponseWrapper message) {
		
		//logger.info("logMessage "+logEntry+":"+message);
		try {
			this.logMessagingService.logMessage(logEntry);
			//this.logMessagingService.logMessagePayload(logEntry, null, ToStringBuilder.reflectionToString(message, ToStringStyle.MULTI_LINE_STYLE));
		} catch (ServiceException e) {			
			logger.error("Could not save to the Broker Message Log", e);
		}		
		
	}

	protected final void logMessagePayload(MsdsMessageLogEntry logEntry, VadirServiceResponseWrapper message) {
		//logger.info("logMessagePayload "+logEntry+":"+message);
		try {
			this.logMessagingService.logMessagePayload(logEntry, null, ToStringBuilder.reflectionToString(message, ToStringStyle.MULTI_LINE_STYLE));
		} catch (ServiceException e) {			
			logger.error("Could not save to the Broker Message Payload Log", e);
		}

	}
	
	/**
	 * Method to log the message.
	 * 
	 * @param logEntry
	 *           The message log entry.
	 */
	protected final void logErrorMessage(MsdsErrorLogEntry errorLogEntry) {
		//logger.error("logErrorMessage "+errorLogEntry);
		try {
			this.logMessagingService.logMessage(errorLogEntry);
		} catch (ServiceException e) {			
			logger.error("Could not save to Broker Message Log", e);			
		}
	}

	public LogTransmissionService getLogMessagingService() {
		return this.logMessagingService;
	}

	public void setLogMessagingService(
			LogTransmissionService logMessagingService) {
		this.logMessagingService = logMessagingService;
	}

	/**
	 * @return Returns the personService.
	 */
	public PersonService getPersonService() {
		return personService;
	}

	/**
	 * @param personService The personService to set.
	 */
	public void setPersonService(PersonService personService) {
		this.personService = personService;
	}

	public LookupService getLookupService() {
		return this.lookupService;
	}

	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}
	/**
	 * @return the militaryInfoService
	 */
	public MilitaryInfoService getMilitaryInfoService() {
		return militaryInfoService;
	}

	/**
	 * @param militaryInfoService the militaryInfoService to set
	 */
	public void setMilitaryInfoService(MilitaryInfoService militaryInfoService) {
		this.militaryInfoService = militaryInfoService;
	}

	/**
	 * @return the militaryServiceBuilderForMSDS
	 */
	public MilitaryServiceBuilderForMSDS getMilitaryServiceBuilderForMSDS() {
		return militaryServiceBuilderForMSDS;
	}

	/**
	 * @param militaryServiceBuilderForMSDS the militaryServiceBuilderForMSDS to set
	 */
	public void setMilitaryServiceBuilderForMSDS(
			MilitaryServiceBuilderForMSDS militaryServiceBuilderForMSDS) {
		this.militaryServiceBuilderForMSDS = militaryServiceBuilderForMSDS;
	}

	
}
