package com.eactive.eai.adapter.amq;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.jms.pool.PooledConnectionFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.jms.listener.DefaultMessageListenerContainer;

import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.RequestDispatcher;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.agent.command.CommandException;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.Logger;

public class AMQReceiverAdapter {
	private static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);

	private AdapterVO adapterVo;
	DefaultMessageListenerContainer listenerContainer = null;

	public AMQReceiverAdapter(AdapterVO adapterVo) {
		this.adapterVo = adapterVo;
	}

	public void start() throws Exception {
		AdapterPropManager propManager = AdapterPropManager.getInstance();
		Properties queueProp = propManager.getProperties(adapterVo.getPropGroupName());
		String brokerUrl = queueProp.getProperty(AMQAdapterConstants.PROP_BROKER_URL);
		if (StringUtils.isBlank(brokerUrl)) {
			throw new CommandException(AMQAdapterConstants.PROP_BROKER_URL + " is a required.");
		}
		String userName = queueProp.getProperty(AMQAdapterConstants.PROP_USER_NAME);
		String password = queueProp.getProperty(AMQAdapterConstants.PROP_PASSWORD);
		String receiveQueue = queueProp.getProperty(AMQAdapterConstants.PROP_RECEIVE_QUEUE_NAME);
		if (StringUtils.isBlank(receiveQueue)) {
			throw new Exception("No have receive Queue name");
		}
		String concurrency = queueProp.getProperty(AMQAdapterConstants.PROP_RECEIVE_MAX_CONCURRENT_CONSUMERS);
		String replyQueue = queueProp.getProperty(AMQAdapterConstants.PROP_REPLY_QUEUE_NAME);
		String relayPropertyGroup = queueProp.getProperty(AMQAdapterConstants.PROP_RELAY_PROPERTY_GROUP);
		String relayRequestPropertyKeys = queueProp.getProperty(AMQAdapterConstants.PROP_RELAY_REQUEST_PROPERTY_KEYS);

		// Queue Connection pool 생성
		ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
		if (StringUtils.isNotBlank(userName)) {
			connectionFactory.setUserName(userName);
			connectionFactory.setPassword(StringUtils.defaultString(password));
		}
		connectionFactory.setTrustAllPackages(true);

		listenerContainer = new DefaultMessageListenerContainer();
		listenerContainer.setConnectionFactory(connectionFactory);
		listenerContainer.setDestinationName(receiveQueue);
		if (NumberUtils.isParsable(concurrency)) {
			listenerContainer.setConcurrency(concurrency);
		}
		listenerContainer.setMessageListener(new MessageListener() {
			@Override
			public void onMessage(Message message) {
				try {
					if (message instanceof TextMessage) {
						if (logger.isDebug()) {
							logger.debug("Received from " + receiveQueue + ": " + ((TextMessage) message).getText());
						}

						String receivedMsg = convertMessageToString(message);

						Properties prop = new Properties();
						// RestDataAdapterGroupParseRequestAction 오류 방지 위해 set
						prop.setProperty(HttpAdapterServiceKey.INBOUND_METHOD, "QUEUE");

						Object returnObject = RequestDispatcher.getRequestDispatcher(adapterVo.getAdapterGroupName())
								.handle(adapterVo.getName(), receivedMsg, prop);

						// reply queue가 설정되어 있으면(Sync) 전송
						if (StringUtils.isNotBlank(replyQueue)) {
							replyMessage((String) returnObject, replyQueue, relayPropertyGroup);
						}
					} else {
						logger.error("Only supports TextMessage.");
						throw new IllegalArgumentException(
								"Only supports TextMessage: " + message.getClass().getName());
					}
				} catch (Exception e) {
					logger.error(e.getMessage());
					throw new RuntimeException(e);
				}
			}

			@SuppressWarnings({ "deprecation", "unchecked" })
			private String convertMessageToString(Message message) throws Exception {
				String strMsg = ((TextMessage) message).getText();

				// Queue property relay
				if (MessageType.JSON.equals(adapterVo.getAdapterGroupVO().getMessageType())
						&& StringUtils.isNotBlank(relayPropertyGroup)
						&& StringUtils.isNotBlank(relayRequestPropertyKeys)) {
					JSONObject jsonMessage = (JSONObject) JSONValue.parse(StringUtils.defaultString(strMsg));
					JSONObject propertyJson = new JSONObject();
					if (StringUtils.equalsIgnoreCase(relayRequestPropertyKeys, "ALL")) {
						Enumeration<String> propertyNames = message.getPropertyNames();
						while (propertyNames.hasMoreElements()) {
							try {
								String propertyName = propertyNames.nextElement();
								propertyJson.put(propertyName, message.getStringProperty(propertyName));
							} catch (Exception e) {
								if (logger.isWarn()) {
									logger.warn(e.getMessage());
								}
							}
						}
					} else {
						String[] relayKeyArr = org.springframework.util.StringUtils
								.tokenizeToStringArray(relayRequestPropertyKeys, ",");
						for (String key : relayKeyArr) {
							try {
								String propertyValue = message.getStringProperty(key);
								if (StringUtils.isNotBlank(propertyValue)) {
									propertyJson.put(key, propertyValue);
								}
							} catch (Exception e) {
								if (logger.isWarn()) {
									logger.warn(e.getMessage());
								}
							}
						}
					}

					if (propertyJson.size() > 0) {
						jsonMessage.put(relayPropertyGroup, propertyJson);
						strMsg = jsonMessage.toJSONString();
					}
				}

				return strMsg;
			}

			private void replyMessage(String replyMessage, String replyQueue, String relayPropertyGroup)
					throws Exception {
				PooledConnectionFactory connectionFactory = AMQAdapterManager.getInstance()
						.getOutboundPooledConnectionFactory(adapterVo.getName());

				JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
				jmsTemplate.setExplicitQosEnabled(true);
				jmsTemplate.setDeliveryPersistent(false);

				jmsTemplate.send(replyQueue, new MessageCreator() {
					@SuppressWarnings("deprecation")
					public Message createMessage(Session session) throws JMSException {
						String strMsg = replyMessage;

						// relay to Queue property
						HashMap<String, String> properties = new HashMap<>();
						if (MessageType.JSON.equals(adapterVo.getAdapterGroupVO().getMessageType())
								&& StringUtils.isNotBlank(relayPropertyGroup)
								&& StringUtils.contains(replyMessage, relayPropertyGroup)) {
							try {
								JSONObject dataObject = (JSONObject) JSONValue.parse(replyMessage);
								JSONObject propertyObject = (JSONObject) dataObject.get(relayPropertyGroup);
								if (propertyObject != null) {
									for (Object key : propertyObject.keySet()) {

										Object obj = propertyObject.get(key);
										if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) {
											continue;
										}

										properties.put((String) key, (String) obj);
									}

									dataObject.remove(relayPropertyGroup);
									strMsg = dataObject.toJSONString();
								}
							} catch (Exception e) {
								if (logger.isWarn()) {
									logger.warn(e.getMessage());
								}
							}
						}

						Message ms = session.createTextMessage(strMsg);
						for (Map.Entry<String, String> entry : properties.entrySet()) {
							ms.setStringProperty(entry.getKey(), entry.getValue());
						}

						return ms;
					}
				});
			}
		});

		listenerContainer.afterPropertiesSet();
		listenerContainer.start();
	}

	public void stop() throws Exception {
		if (listenerContainer != null) {
			listenerContainer.stop();
			listenerContainer.shutdown();
		}
	}
}
