package com.ejie.ab04b.mdb;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.ApplicationException;
import javax.ejb.EJBException;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.interceptor.Interceptors;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor;

import com.ejie.ab04b.constantes.ConstantesEventos;
import com.ejie.ab04b.evento.EventoAvisos;
import com.ejie.ab04b.evento.EventoAvisosService;
import com.ejie.ab04b.parse.MessageParser;
import com.ejie.ab04b.service.eventos.DeadMessageService;
import com.ejie.ab04b.util.PropertiesUtil;
import com.ejie.foldermanagement.xml.ContextSubmissionEvent;
import com.ejie.mbt.xml.Task;
import com.ejie.notification.xml.NotificationBody;

/**
 * Message-Driven Bean implementation class for: AvisosSesionMdb MDB encargado
 * de tratar los mensajes entrantes por la cola de integracion destinada a los
 * eventos consecuencia de las acciones con respuesta asincrona con
 * PLATEA-Tramitacion
 * 
 * @author GFI
 */
@MessageDriven(messageListenerInterface = javax.jms.MessageListener.class, name = ConstantesEventos.COLA_AVISOS_NAME_AB04B, mappedName = ConstantesEventos.COLA_AVISOS_JNDI_AB04B, activationConfig = {
		@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
		@ActivationConfigProperty(propertyName = "connectionFactoryJndiName", propertyValue = ConstantesEventos.JMS_CONNECTION_FACTORY_JNDI_AB04B) })
@TransactionManagement(value = TransactionManagementType.CONTAINER)
@TransactionAttribute(value = TransactionAttributeType.REQUIRED)
@ApplicationException(rollback = true)
@Interceptors(value = SpringBeanAutowiringInterceptor.class)
public class AvisosSesionMdb implements MessageListener {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(AvisosSesionMdb.class);

	private transient MessageDrivenContext messageDrivenContext = null;

	@Autowired()
	private DeadMessageService deadMessageService;

	@Autowired()
	private EventoAvisosService eventoAvisosService;

	/**
	 * Sets the message driven context.
	 *
	 * @param arg0            contexto del bean
	 * @throws EJBException             en caso de error
	 * @see javax.ejb.MessageDrivenBean#setMessageDrivenContext(javax.ejb.MessageDrivenContext)
	 */
	public void setMessageDrivenContext(MessageDrivenContext arg0)
			throws EJBException {
		this.messageDrivenContext = arg0;
	}

	/**
	 * Contiene la logica de tratamiento de los mensajes entrantes. La
	 * responsabilidad de la transaccionalidad se delega en un servicio
	 *
	 * @param message            mensaje que llega a la cola de la que lee el MDB
	 * @see MessageListener#onMessage(Message)
	 */
	// CHECKSTYLE:OFF
	public void onMessage(Message message) {

		AvisosSesionMdb.LOGGER.info("onMessage - Inicio");
		// comprobamos si es necesario tratar el mensaje o no
		try {
			if (message.getJMSRedelivered()) {
				// se trata de un reintento solamente se dejara que reintente
				// hasta un numero determinado de veces, hacemos tratamiento
				// manual para cuando se llegue a ese limite. El tratamiento
				// manual del error es adrede, la decision tecnica pasa por no
				// cambiar las configuraciones de colas del servidor de EJIE
				int deliveryCount = message.getIntProperty("JMSXDeliveryCount");
				String numReintentosMax = PropertiesUtil
						.getProperty(PropertiesUtil.NUM_REINTENTOS_JMS_AVISOS);
				AvisosSesionMdb.LOGGER.info("Número máximo de reintentos: {}",
						numReintentosMax);
				AvisosSesionMdb.LOGGER.info("Número de intento actual: {}",
						deliveryCount);

				if ((null != numReintentosMax)
						&& (deliveryCount >= Integer.parseInt(numReintentosMax))) {
					// hemos llegado al limite de reintentos configurados en
					// el
					// sistema
					AvisosSesionMdb.LOGGER
							.warn("onMessage - Llega mensaje con numero maximo de reintentos ya agotados");

					String maxLimiteIntento = PropertiesUtil
							.getProperty(PropertiesUtil.NUM_REINTENTOS_JMS_LIMITE);
					// Si se supera un límite (por defecto, 100) se dejará de
					// intentar salvar el mensaje y se descartará totalmente
					if (maxLimiteIntento == null
							|| deliveryCount < Integer
									.parseInt(maxLimiteIntento)) {
						if (!this.deadMessageService
								.tratarDeadMessage(
										ConstantesEventos.ERROR_EVENTO_COLA_TIPO_AVISOS,
										message)) {
							// por algun motivo no se ha realizado el
							// tratamiento
							// correctamente, que se encole
							AvisosSesionMdb.LOGGER
									.error("onMessage - No se ha podido finalizar el tratamiento del mensaje con reintentos agotados");
							this.messageDrivenContext.setRollbackOnly();
						}
					}
					return;
				}
			}
		} catch (JMSException jmsex) {
			AvisosSesionMdb.LOGGER.error(
					"onMessage - error al coger el numero de reintento", jmsex);
			this.messageDrivenContext.setRollbackOnly();
		}
		// es un mensaje que hay que tratar

		// Obtenemos el objeto NotificationBody
		NotificationBody notificationBody = null;
		boolean errorTratamiento = false;
		try {
			MessageParser<NotificationBody> parserNotification = new MessageParser<NotificationBody>(
					NotificationBody.class.getPackage().getName());
			notificationBody = parserNotification.parse(((TextMessage) message)
					.getText());
		} catch (Exception pe) {
			AvisosSesionMdb.LOGGER.error(
					"onMessage - error al parsear el mensaje de la cola", pe);
			errorTratamiento = true;
		}

		try {
			if (!errorTratamiento) {

				// Creamos el objeto EventoAvisos con la informacion de la
				// cabecera del evento

				String strWhat = message
						.getStringProperty(ConstantesEventos.EVENTO_EVENT_WHAT);
				String strWho = message
						.getStringProperty(ConstantesEventos.EVENTO_EVENT_WHO);
				String strTipo = message
						.getStringProperty(ConstantesEventos.EVENTO_EVENT_TIPOLOGY);
				String strPlateaType = message
						.getStringProperty(ConstantesEventos.EVENTO_PLATEA_TYPE);
				String strPlateaSubType = message
						.getStringProperty(ConstantesEventos.EVENTO_PLATEA_SUBTYPE);
				String strStatus = message
						.getStringProperty(ConstantesEventos.EVENTO_STATUS);
				String strMailbox = message
						.getStringProperty(ConstantesEventos.EVENTO_MAILBOX);

				if (strWhat == null || strWhat.trim().length() == 0) {
					AvisosSesionMdb.LOGGER
							.info("onMessage - strWhat de Mensaje es NULO");
					strWhat = notificationBody
							.getPropertyValue(ConstantesEventos.EVENTO_EVENT_WHAT);
				}
				if (strWho == null || strWho.trim().length() == 0) {
					AvisosSesionMdb.LOGGER
							.info("onMessage - strWho de Mensaje es NULO");
					strWho = notificationBody
							.getPropertyValue(ConstantesEventos.EVENTO_EVENT_WHO);
				}
				if (strTipo == null || strTipo.trim().length() == 0) {
					AvisosSesionMdb.LOGGER
							.info("onMessage - strTipo de Mensaje es NULO");
					strTipo = notificationBody
							.getPropertyValue(ConstantesEventos.EVENTO_EVENT_TIPOLOGY);
				}
				if (strPlateaType == null || strPlateaType.trim().length() == 0) {
					AvisosSesionMdb.LOGGER
							.info("onMessage - strPlateaType de Mensaje es NULO");
					strPlateaType = notificationBody
							.getPropertyValue(ConstantesEventos.EVENTO_PLATEA_TYPE);
				}
				if (strPlateaSubType == null
						|| strPlateaSubType.trim().length() == 0) {
					AvisosSesionMdb.LOGGER
							.info("onMessage - strPlateaSubtype de Mensaje es NULO");
					strPlateaSubType = notificationBody
							.getPropertyValue(ConstantesEventos.EVENTO_PLATEA_SUBTYPE);
				}
				if (strStatus == null || strStatus.trim().length() == 0) {
					AvisosSesionMdb.LOGGER
							.info("onMessage - strStatus de Mensaje es NULO");
					strStatus = notificationBody
							.getPropertyValue(ConstantesEventos.EVENTO_STATUS);
				}
				if (strMailbox == null || strMailbox.trim().length() == 0) {
					AvisosSesionMdb.LOGGER
							.info("onMessage - strMailbox de Mensaje es NULO");
					strMailbox = notificationBody
							.getPropertyValue(ConstantesEventos.EVENTO_MAILBOX);
				}

				Object[] params = new Object[] { strWhat, strWho, strTipo,
						strPlateaType, strPlateaSubType, strStatus, strMailbox };
				AvisosSesionMdb.LOGGER
						.info("onMessage --> what: {}, who: {}, tipo: {}, plateaType: {}, plateaSubType: {}, status: {}, mailbox: {}",
								params);

				EventoAvisos eventoAvisos = new EventoAvisos(strWhat, strWho,
						strTipo, strPlateaType, strPlateaSubType, strStatus,
						strMailbox);

				if (ConstantesEventos.EVENT_WHO_R02.equalsIgnoreCase(strWho)
						&& ConstantesEventos.EVENT_TIPOLOGY_TRAMITACION
								.equalsIgnoreCase(strTipo)) {

					if (ConstantesEventos.EVENT_WHAT_SOLIC_APORT
							.equalsIgnoreCase(strWhat)) {

						AvisosSesionMdb.LOGGER
								.info("onMessage - Evento de Solicitud o Aportación Telemática");

						if (ConstantesEventos.EVENT_R02_REQUEST_PLATEA_TYPE_SOLIC
								.equals(strPlateaType)) {
							// Es un evento de Solicitud Telemática
							this.tratarEventoSolicitudTelematica(eventoAvisos,
									notificationBody);
						} else if (ConstantesEventos.EVENT_R02_REQUEST_PLATEA_TYPE_SOLIC_PRESENCIAL
								.equals(strPlateaType)) {
							// Es un evento de Solicitud Presencial
							this.tratarEventoSolicitudPresencial(eventoAvisos,
									notificationBody);
						} else if (ConstantesEventos.EVENT_R02_REQUEST_PLATEA_TYPE_APORT
								.equals(strPlateaType)) {
							// Es un evento de Aportacion
							this.tratarEventoAportacion(eventoAvisos,
									notificationBody);
						} else if (ConstantesEventos.EVENT_R02_REQUEST_PLATEA_TYPE_APORT_PRESENCIAL
								.equals(strPlateaType)) {
							// Es un evento de Aportacion
							this.tratarEventoAportacionPresencial(eventoAvisos,
									notificationBody);
						}

					} else if (ConstantesEventos.EVENT_WHAT_TAREAS
							.equalsIgnoreCase(strWhat)) {

						AvisosSesionMdb.LOGGER
								.info("onMessage - Evento de Tareas");

						if (ConstantesEventos.EVENT_R02_TASK_STATUS_SUGERIDA
								.equals(strStatus)) {
							// Es un evento de tarea sugerida
							this.tratarEventoTareaSugerida(eventoAvisos,
									notificationBody);
						} else if (ConstantesEventos.EVENT_R02_TASK_STATUS_EJECUTADA
								.equals(strStatus)) {
							// Es un evento de tarea ejecutada
							this.tratarEventoTareaEjecutada(eventoAvisos,
									notificationBody);
						}
					}
				} else if (ConstantesEventos.EVENT_WHO_TRANSFORMACION
						.equals(strWho)
						&& ConstantesEventos.EVENT_WHAT_TRANSFORMACION
								.equalsIgnoreCase(strWhat)
						&& ConstantesEventos.EVENT_TIPOLOGY_TRANSFORMACION
								.equals(strTipo)) {
					// Es un evento de transformacion
					AvisosSesionMdb.LOGGER
							.info("onMessage - Evento de transformación PDFA");
					this.tratarEventoTransformacion(eventoAvisos,
							notificationBody);
				} else {
					AvisosSesionMdb.LOGGER
							.info("onMessage - El tipo de mensaje no está soportado");
				}
			}

			// message.acknowledge();
			AvisosSesionMdb.LOGGER.info("onMessage - Fin");
		} catch (Exception ex) {
			AvisosSesionMdb.LOGGER.error("onMessage - error en el tratamiento",
					ex);
			this.messageDrivenContext.setRollbackOnly();
		}
	}

	/**
	 * Extrae del mensaje del evento la informacion necesaria para tratar el
	 * evento producido por una aportacion, y guarda esta informacion en el
	 * objeto eventoAvisos.
	 *
	 * @param eventoAvisos            EventoAvisos
	 * @param notificationBody            NotificationBody
	 * @throws Exception the exception
	 */

	private void tratarEventoAportacion(EventoAvisos eventoAvisos,
			NotificationBody notificationBody) throws Exception {

		// 1º obtenemos el contextSubmissionEvent que hay dentro del tag
		// <xmlValue>
		ContextSubmissionEvent contextSubmissionEvent = null;
		MessageParser<ContextSubmissionEvent> parserContextSubmissionEvent = new MessageParser<ContextSubmissionEvent>(
				ContextSubmissionEvent.class.getPackage().getName());
		contextSubmissionEvent = parserContextSubmissionEvent
				.parse(notificationBody.getXmlValue());

		this.eventoAvisosService.gestionarAportacion(contextSubmissionEvent);

	}

	/**
	 * Extrae del mensaje del evento la informacion necesaria para tratar el
	 * evento producido por una aportacion, y guarda esta informacion en el
	 * objeto eventoAvisos.
	 *
	 * @param eventoAvisos            EventoAvisos
	 * @param notificationBody            NotificationBody
	 * @throws Exception the exception
	 */

	private void tratarEventoAportacionPresencial(EventoAvisos eventoAvisos,
			NotificationBody notificationBody) throws Exception {

		// 1º obtenemos el contextSubmissionEvent que hay dentro del tag
		// <xmlValue>
		ContextSubmissionEvent contextSubmissionEvent = null;
		MessageParser<ContextSubmissionEvent> parserContextSubmissionEvent = new MessageParser<ContextSubmissionEvent>(
				ContextSubmissionEvent.class.getPackage().getName());
		contextSubmissionEvent = parserContextSubmissionEvent
				.parse(notificationBody.getXmlValue());

		this.eventoAvisosService
				.gestionarAportacionPresencial(contextSubmissionEvent);

	}

	/**
	 * Extrae del mensaje del evento la informacion necesaria para tratar el
	 * evento producido por una solicitud presencial, y guarda esta informacion
	 * en el objeto eventoAvisos.
	 *
	 * @param eventoAvisos            EventoAvisos
	 * @param notificationBody            NotificationBody
	 * @throws Exception             e
	 */
	private void tratarEventoSolicitudPresencial(EventoAvisos eventoAvisos,
			NotificationBody notificationBody) throws Exception {

		// 1º obtenemos el contextSubmissionEvent que hay dentro del tag
		// <xmlValue>
		ContextSubmissionEvent contextSubmissionEvent = null;
		MessageParser<ContextSubmissionEvent> parserContextSubmissionEvent = new MessageParser<ContextSubmissionEvent>(
				ContextSubmissionEvent.class.getPackage().getName());
		contextSubmissionEvent = parserContextSubmissionEvent
				.parse(notificationBody.getXmlValue());

		this.eventoAvisosService
				.gestionarSolicitudPresencial(contextSubmissionEvent);

	}

	/**
	 * Extrae del mensaje del evento la informacion necesaria para tratar el
	 * evento producido por una solicitud telemática, y guarda esta informacion
	 * en el objeto eventoAvisos.
	 *
	 * @param eventoAvisos            EventoAvisos
	 * @param notificationBody            NotificationBody
	 * @throws Exception             e
	 */
	private void tratarEventoSolicitudTelematica(EventoAvisos eventoAvisos,
			NotificationBody notificationBody) throws Exception {

		// 1º obtenemos el contextSubmissionEvent que hay dentro del tag
		// <xmlValue>
		ContextSubmissionEvent contextSubmissionEvent = null;
		MessageParser<ContextSubmissionEvent> parserContextSubmissionEvent = new MessageParser<ContextSubmissionEvent>(
				ContextSubmissionEvent.class.getPackage().getName());
		contextSubmissionEvent = parserContextSubmissionEvent
				.parse(notificationBody.getXmlValue());

		this.eventoAvisosService
				.gestionarSolicitudTelematica(contextSubmissionEvent);

	}

	/**
	 * Extrae del mensaje del evento la informacion necesaria para tratar el
	 * evento producido por una tarea sugerida, y guarda esta informacion en el
	 * objeto eventoAvisos.
	 *
	 * @param eventoAvisos            EventoAvisos
	 * @param notificationBody            NotificationBody
	 * @throws Exception             e
	 */
	private void tratarEventoTareaSugerida(EventoAvisos eventoAvisos,
			NotificationBody notificationBody) throws Exception {

		// 1º obtenemos la tarea (Task) que hay dentro del tag
		// <xmlValue>
		Task task = null;
		MessageParser<Task> parserTask = new MessageParser<Task>(Task.class
				.getPackage().getName());
		task = parserTask.parse(notificationBody.getXmlValue());

		// 2º gestionamos el evento
		this.eventoAvisosService.gestionarTareaSugerida(eventoAvisos, task);

	}

	/**
	 * Extrae del mensaje del evento la informacion necesaria para tratar el
	 * evento producido por una tarea ejecutada, y guarda esta informacion en el
	 * objeto eventoAvisos.
	 *
	 * @param eventoAvisos            EventoAvisos
	 * @param notificationBody            NotificationBody
	 * @throws Exception             e
	 */
	private void tratarEventoTareaEjecutada(EventoAvisos eventoAvisos,
			NotificationBody notificationBody) throws Exception {

		// 1º obtenemos la tarea (Task) que hay dentro del tag
		// <xmlValue>
		Task task = null;
		MessageParser<Task> parserTask = new MessageParser<Task>(Task.class
				.getPackage().getName());
		task = parserTask.parse(notificationBody.getXmlValue());

		// 2º gestionamos el evento
		this.eventoAvisosService.gestionarTareaEjecutada(eventoAvisos, task);

	}

	/**
	 * Extrae del mensaje del evento la informacion necesaria para tratar el
	 * evento producido por la transformación de un documento de Dokusi, y
	 * guarda esta informacion en el objeto eventoAvisos.
	 *
	 * @param eventoAvisos            EventoAvisos
	 * @param notificationBody            NotificationBody
	 * @throws Exception             e
	 */
	private void tratarEventoTransformacion(EventoAvisos eventoAvisos,
			NotificationBody notificationBody) throws Exception {

		// Document OID
		String oid = null;
		if (notificationBody
				.getPropertyValue(ConstantesEventos.EVENTO_PROPERTY_DOCUMENT_ID) != null) {
			oid = notificationBody
					.getPropertyValue(ConstantesEventos.EVENTO_PROPERTY_DOCUMENT_ID);
		}

		this.eventoAvisosService.gestionarEventoTransformacionPDFA(oid);
	}

}
