package gov.va.med.esr.jms.vadir;

import gov.va.med.esr.jms.vadir.inboundRequest.VadirServiceMilitaryHistoryRequest;
import gov.va.med.esr.jms.vadir.outboundResponse.VadirServiceResponseType;
import gov.va.med.esr.jms.vadir.outboundResponse.VadirServiceResponseWrapper;
import gov.va.med.esr.webservices.client.vadir.VadirWSClient;
import gov.va.schema.militaryhistory.MilitaryHistory;
import gov.va.schema.militaryhistory.ServicePeriod;
import gov.va.schema.militaryhistoryservice.SearchCriteria;

import java.io.Serializable;
import java.util.Calendar;

import java.util.GregorianCalendar;

import javax.annotation.Resource;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;

//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Component;
import org.springframework.ws.client.WebServiceClientException;
import org.springframework.ws.client.WebServiceTransformerException;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.log4j.Logger;


/**
 * @author DNS   barryc
 */
@Component
public class VadirRequestJMSListener implements MessageListener {
	static final String SERVICE_NOT_AVAILABLE_MESSAGE = "ServiceNotAvailableMessage";

	private static final Logger logger = Logger
			.getLogger(VadirRequestJMSListener.class);

	// ------------------------------------------- Fields

	@Autowired
	private VadirWSClient client;
	@Resource(name = "vadirOutboundJMSTemplate")
	private JmsTemplate jmsTemplate;
	@Autowired
	private StubTranslationService stubTranslationService;

	// ------------------------------------------- Business Methods

	@Override
	public void onMessage(Message message) {
		logger.trace("Received JMS message {}"+ message);

		if (message instanceof ObjectMessage == false) {
			sendErrorResponse(0, 0, "Message must be of type "
					+ ObjectMessage.class.getName());
			return;
		}

		ObjectMessage om = (ObjectMessage) message;

		Serializable o = null;
		try {
			o = om.getObject();
		} catch (JMSException e) {
			logger.error("Unexpected exception while reading JMS message", e);
			sendErrorResponse(
					0,
					0,
					"Unexpected exception while reading JMS message: "
							+ e.getMessage());
			return;
		}

		if (o instanceof VadirServiceMilitaryHistoryRequest == false) {
			sendErrorResponse(0, 0, "Message must contain a "
					+ VadirServiceMilitaryHistoryRequest.class.getName()
					+ ", but instead contained a " + o.getClass().getName());
			return;
		}

		VadirServiceMilitaryHistoryRequest vsr = (VadirServiceMilitaryHistoryRequest) o;

		MilitaryHistory mh = null;
		logger.debug("Requesting Military Service Data for:" + "Request Id:" + vsr.getRequestId() + "," + "Person ID:" + vsr.getPersonId());
		try {


			mh = getMilitaryHistoryFromVadir(vsr);

		//} catch (WebServiceClientException e) {
		} catch (Exception e) {
			/* SIC */

			if ("ServiceFault".equals(e.getMessage())
					|| "ServiceNotAvaialbleBIRLS".equals(e.getMessage())
					|| "ServiceNotAvaialbleVADIR".equals(e.getMessage())
					|| e.getMessage().contains("TimedOutException")) {
				handleServiceNotAvailable(message, e);
			} else {
				sendErrorResponse(vsr.getRequestId(), vsr.getPersonId(),
						e.getMessage());
			}
			return;
		}

		if ((mh == null) || (mh.getVADIR() == null && mh.getBIRLS() == null)) {
			logger.debug("No results found.");
			sendErrorResponse(vsr.getRequestId(), vsr.getPersonId(),
					"NoPersonFound");
		} else {
			gov.va.med.esr.jms.vadir.outboundResponse.model.militaryHistory.MilitaryHistory esrMilitaryHistory = stubTranslationService
					.translate(mh);
			sendResponse(new VadirServiceResponseWrapper(vsr.getRequestId(),
					vsr.getPersonId(), esrMilitaryHistory));
		}
	}

	private void handleServiceNotAvailable(Message message, Exception e)
			throws RuntimeException {
		logger.warn("Service Not Available: " + e.getMessage(), e);
		throw new RuntimeException(
				"Rolling back transaction and returning message to queue");
	}

	private void sendResponse(final VadirServiceResponseWrapper r) {
		logger.debug("Sending response {}"+ r);
		try {
			jmsTemplate.send(new VadirMessageCreator(r));
		} catch (RuntimeException e) {
			logger.error("Exception sending response: " + e.getMessage(), e);
		}
	}

	private void sendErrorResponse(long requestId, long personId, String error) {
		sendResponse(new VadirServiceResponseWrapper(requestId, personId,
				VadirServiceResponseType.REQUEST_ERROR, error));
	}

	private MilitaryHistory getMilitaryHistoryFromVadir(
			VadirServiceMilitaryHistoryRequest vsr)
			throws WebServiceClientException {
		SearchCriteria sc = new SearchCriteria();

		if (vsr.getDateOfBirth() != null) {
			GregorianCalendar cal = new GregorianCalendar();
			cal.setTime(vsr.getDateOfBirth());
			try {
				sc.setDateOfBirth(DatatypeFactory.newInstance()
						.newXMLGregorianCalendarDate(cal.get(Calendar.YEAR),
								cal.get(Calendar.MONTH) + 1,
								cal.get(Calendar.DATE),
								DatatypeConstants.FIELD_UNDEFINED));
			} catch (DatatypeConfigurationException e) {
				throw new WebServiceTransformerException(
						"Problem instantiating a "
								+ DatatypeFactory.class.getSimpleName());
			}
		}

		sc.setFirstName(vsr.getFirstName());
		sc.setGender(vsr.getGender());
		sc.setICN(vsr.getIcn());
		sc.setLastName(vsr.getLastName());
		sc.setSSN(vsr.getSsn());

		MilitaryHistory mh = client.getMilitaryHistory(sc);
		return mh;
	}

	public static final class VadirMessageCreator implements MessageCreator {
		private final VadirServiceResponseWrapper r;

		public VadirMessageCreator(VadirServiceResponseWrapper r) {
			this.r = r;
		}

		public Message createMessage(Session session) throws JMSException {
			ObjectMessage message = session.createObjectMessage(r);
			message.setStringProperty("Message_Type", "MSDS");
			return message;
		}
	}
}
