package com.eactive.eai.flowcontrol.jms;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.jms.Session;

import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.eactive.eai.common.activemq.AmqPooledConnectionFactory;
import com.eactive.eai.common.dao.DAOException;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.lifecycle.Lifecycle;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.lifecycle.LifecycleListener;
import com.eactive.eai.common.lifecycle.LifecycleSupport;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.routing.RouteKeys;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.env.ElinkConfig;

@Component
public class FCQueueReceiverManager implements Lifecycle {
	public static final int MQ_ACTIVEMQ = 0;
	public static final int MQ_JMS = 1;
	public static final int MQ_RABBITMQ = 2;

	private static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

	private Map<String, FCQueueReceiverVO> receiverVos = new HashMap<>();
	private HashMap<String, Object> receivers = new HashMap<>();

	private LifecycleSupport lifecycle = new LifecycleSupport(this);
	private boolean started;

	@Autowired
	FCQueueReceiverDAO fcQueueReceiverDAO;

	public static FCQueueReceiverManager getInstance() {
		return ApplicationContextProvider.getContext().getBean(FCQueueReceiverManager.class);
	}

	private String getConnectionURL() {
		String connectionUrl = PropManager.getInstance().getProperty(RouteKeys.GROUP_NAME,
				RouteKeys.ROUTING_QUEUE_CONNECTION_FACTORY);
		if (connectionUrl == null || ElinkConfig.isUseInternalQueue()) {
			connectionUrl = AmqPooledConnectionFactory.CONNECTION_URI;
		}

		return connectionUrl;
	}

	private int getMqType() {
		String connectionUri = getConnectionURL();
		int type = MQ_ACTIVEMQ;

		if (connectionUri == null) {
			return MQ_ACTIVEMQ;
		}

		if (connectionUri.startsWith("tcp:")) {
			type = MQ_ACTIVEMQ;
		} else if (connectionUri.startsWith("vm:")) {
			type = MQ_ACTIVEMQ;
		} else if (connectionUri.startsWith("amqp:")) {
			type = MQ_RABBITMQ;
		} else {
			type = MQ_JMS;
		}

		return type;
	}

	public void start() throws LifecycleException {
		if (started)
			throw new LifecycleException("RECEAICFM201");

		lifecycle.fireLifecycleEvent(STARTING_EVENT, this);

		try {
			receiverVos = fcQueueReceiverDAO.getAllReceivers();
		} catch (DAOException e) {
			throw new LifecycleException(ExceptionUtil.getErrorCode(e, "RECEAICFM202"));
		}

		Iterator<String> it = receiverVos.keySet().iterator();
		String key = "";
		while (it.hasNext()) {
			key = it.next();
			FCQueueReceiverVO vo = receiverVos.get(key);
			try {
				if ("1".equals(vo.getBascQueRcverYn())) {
					startReceiver(vo.getReceiverName(), vo.getQueueName(), vo.getErrorQueueName(),
							vo.getReceiverCount(), vo.getErrorDelayTimeMs(), vo.getRetryCount(),
							vo.getMessageSelector(), vo.getDescription());
				} else {
					if (logger.isWarn()) {
						logger.warn("FCQueueReceiverManager] Error Occurred while start FCReceiver["
								+ vo.getReceiverName() + "] 사용안함 ");
					}
				}
			} catch (Exception e) {
				logger.error("FCQueueReceiverManager] Error Occured while start FCReceiver[" + key
						+ "] continue start : Error - " + e.getMessage());
			}
		}
		started = true;
		lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
	}

	public void stop() throws LifecycleException {
		if (!started)
			throw new LifecycleException("RECEAICFM203");

		lifecycle.fireLifecycleEvent(STOPING_EVENT, this);

		Iterator<String> it = receiverVos.keySet().iterator();
		String key = "";
		while (it.hasNext()) {
			key = it.next();
			try {
				stopReceiver(key);
			} catch (Exception e) {
				if (logger.isWarn()) {
					logger.warn("FCQueueReceiverManager] Error Occurred while stop FCReceiver[" + key
							+ "] continue start : Error - " + e.getMessage());
				}
			}
		}
		receiverVos.clear();

		started = false;
		lifecycle.fireLifecycleEvent(STOPPED_EVENT, this);
	}

	public void addLifecycleListener(LifecycleListener listener) {
		lifecycle.addLifecycleListener(listener);
	}

	public LifecycleListener[] findLifecycleListeners() {
		return lifecycle.findLifecycleListeners();
	}

	public void removeLifecycleListener(LifecycleListener listener) {
		lifecycle.removeLifecycleListener(listener);
	}

	public boolean isStarted() {
		return this.started;
	}

	public FCQueueReceiverVO getFCQueueReceiver(String receiverName) {
		if (receiverName == null)
			return null;
		return receiverVos.get(receiverName);
	}

	public void loadFCQueueReceiver(String queRcverName) throws Exception {
		FCQueueReceiverVO vo = null;
		try {
			boolean isScalable = BooleanUtils.toBoolean(System.getProperty("scalable"));
			String localServerName ="";
			if(isScalable) {
				localServerName ="ALL";
			}else {
				localServerName = EAIServerManager.getInstance().getLocalServerName();
			}

			vo = fcQueueReceiverDAO.getReceiver(queRcverName);

			if (this.receivers.get(vo.getReceiverName()) != null) {
				stopReceiver(vo.getReceiverName());
			}

			if (localServerName.equals(vo.getEaiSevrInstncName())) {
				receiverVos.put(vo.getReceiverName(), vo);
				if ("1".equals(vo.getBascQueRcverYn())) {
					startReceiver(vo.getReceiverName(), vo.getQueueName(), vo.getErrorQueueName(),
							vo.getReceiverCount(), vo.getErrorDelayTimeMs(), vo.getRetryCount(),
							vo.getMessageSelector(), vo.getDescription());
				} else {
					if (logger.isWarn()) {
						logger.warn("FCQueueReceiverManager] Error Occurred while start FCReceiver["
								+ vo.getReceiverName() + "] 사용안함 ");
					}
				}
			}

		} catch (Exception e) {
			String message = "";
			if (vo != null) {
				message = "FCQueueReceiverManager] Error Occurred while start FCReceiver[" + vo.getReceiverName()
						+ "] - " + e.getMessage();
			} else {
				message = "FCQueueReceiverManager] Error Occurred while start FCReceiver[" + queRcverName + "] - "
						+ e.getMessage();
			}
			logger.error(message, e);

			throw new Exception(message);
		}
	}

	public void setFCQueueReceiver(FCQueueReceiverVO vo) throws Exception {
		try {
			receiverVos.put(vo.getReceiverName(), vo);

			if (this.receivers.get(vo.getReceiverName()) != null) {
				stopReceiver(vo.getReceiverName());
			}

			startReceiver(vo.getReceiverName(), vo.getQueueName(), vo.getErrorQueueName(), vo.getReceiverCount(),
					vo.getErrorDelayTimeMs(), vo.getRetryCount(), vo.getMessageSelector(), vo.getDescription());
		} catch (Exception e) {
			String message = "FCQueueReceiverManager] Error Occurred while start FCReceiver[" + vo.getReceiverName()
					+ "] - " + e.getMessage();
			logger.error(message, e);
			throw new Exception(message);
		}
	}

	public void removeFCQueueReceiver(String receiverName) throws Exception {
		try {
			stopReceiver(receiverName);
			receiverVos.remove(receiverName);
		} catch (Exception e) {
			String message = "FCQueueReceiverManager] Error Occurred while stop FCReceiver[" + receiverName + "] - "
					+ e.getMessage() + "] - " + e.getMessage();
			logger.error(message, e);
			throw new Exception(message);
		}
	}

	public Map<String, FCQueueReceiverVO> getAllFCQueueReceiverVos() {
		return this.receiverVos;
	}

	// -------------------------------------------------------------------
	// receiver control Method
	// -------------------------------------------------------------------
	public void startReceiver(String receiverName, String QueueName, String errorQueueName, int receiverCount,
			int deplayMs, int retryCnt, String selector, String descr) throws Exception {
		try {

			int type = getMqType();

			switch (type) {
			case MQ_RABBITMQ:
				FCRabbitMqReceiver rabbitMqReceiverApp = new FCRabbitMqReceiver(receiverName, QueueName, errorQueueName,
						receiverCount, deplayMs, retryCnt, false, // TxMode
						1, // (Session.AUTO_ACKNOWLEDGE)
						selector, descr);

				rabbitMqReceiverApp.startReceiving();
				receivers.put(receiverName, rabbitMqReceiverApp);
				logger.warn("FCRabbitMqReceiver Start - " + rabbitMqReceiverApp);
				break;
			case MQ_JMS:
				FCQueueReceiver receiverApp = new FCQueueReceiver(receiverName, QueueName, errorQueueName,
						receiverCount, deplayMs, retryCnt, false, // TxMode
						Session.AUTO_ACKNOWLEDGE, selector, descr);

				receiverApp.startReceiving();
				receivers.put(receiverName, receiverApp);
				logger.warn("FCQueueReceiverManager] Start - " + receiverName);
				break;
			default:
				FCActiveMqReceiver activeMqReceiverApp = new FCActiveMqReceiver(receiverName, QueueName, errorQueueName,
						receiverCount, deplayMs, retryCnt, false, // TxMode
						1, // (Session.AUTO_ACKNOWLEDGE)
						selector, descr);

				activeMqReceiverApp.startReceiving();
				receivers.put(receiverName, activeMqReceiverApp);
				logger.warn("FCActiveMqReceiver Start - " + activeMqReceiverApp);
				break;
			}
		} catch (Exception e) {
			logger.error("FCQueueReceiverManager] Start Error", e);
			throw e;
		}
	}

	public void startReceiver(String receiverName) throws Exception {
		try {
			FCQueueReceiverVO vo = fcQueueReceiverDAO.getReceiver(receiverName);

			startReceiver(vo.getReceiverName(), vo.getQueueName(), vo.getErrorQueueName(), vo.getReceiverCount(),
					vo.getErrorDelayTimeMs(), vo.getRetryCount(), vo.getMessageSelector(), vo.getDescription());
			logger.warn("FCQueueReceiverManager] Start - " + receiverName);
		} catch (Exception e) {
			logger.error("FCQueueReceiverManager] Error ", e);
			throw e;
		}
	}

	public void stopReceiver(String receiverName) throws Exception {
		try {
			Object receiverObj = receivers.get(receiverName);

			if (receiverObj instanceof FCQueueReceiver) {
				FCQueueReceiver receiverApp = (FCQueueReceiver) receiverObj;
				receiverApp.stopReceiving();
				receiverApp.close();
				receivers.remove(receiverName);
				logger.warn("FCQueueReceiverManager] FCQueueReceiver Stopped - " + receiverName);

			} else if (receiverObj instanceof FCActiveMqReceiver) {
				FCActiveMqReceiver receiverApp = (FCActiveMqReceiver) receiverObj;
				receiverApp.stopReceiving();
				receiverApp.close();
				receivers.remove(receiverName);
				logger.warn("FCQueueReceiverManager] Receiver Stopped - " + receiverName);

			} else if (receiverObj instanceof FCRabbitMqReceiver) {
				FCRabbitMqReceiver receiverApp = (FCRabbitMqReceiver) receiverObj;
				receiverApp.stopReceiving();
				receiverApp.close();
				receivers.remove(receiverName);
				logger.warn("FCQueueReceiverManager] FCRabbitMqReceiver Stopped - " + receiverName);
			}
		} catch (Exception e) {
			logger.error("FCQueueReceiverManager] Receiver Stop error", e);
			throw e;
		}
	}

	// 개별 Queue Receiver의 Thread별 건수 조회
	public String[][] getMonitorData(String receiverName) throws Exception {
		String[][] status = null;
		// 0 : receiver Thread Name
		// 1 : 메시지 처리건수
		// 2 : 메시지 성공건수
		// 3 : 현재상태 (0 - wait, 1 - running, 2 - retry)
		try {
			FCQueueReceiver receiverApp = (FCQueueReceiver) receivers.get(receiverName);
			if (receiverApp != null) {
				status = receiverApp.getStatus();
			} else {
				throw new Exception("Receiver Already Stopped - " + receiverName);
			}

		} catch (Exception e) {
			logger.error("FCQueueReceiverManager] getMonitorData error", e);
			throw e;
		}
		return status;
	}

	// Queue Receiver의 전체 건수 조회
	public String[][] getMonitorData() throws Exception {
		String[][] status = new String[receivers.size()][3];
		try {
			int i = 0;
			for (Map.Entry<String, Object> entry : receivers.entrySet()) {
				String receiverName = entry.getKey();
				FCQueueReceiver receiverApp = (FCQueueReceiver) entry.getValue();

				if (receiverApp != null) {
					String[] counter = receiverApp.getMessageCount();
					status[i][0] = receiverName;
					status[i][1] = counter[0]; // 메시지 건수
					status[i][2] = counter[1]; // 성공건수
				} else {
					status[i][0] = receiverName;
					status[i][1] = "0";
					status[i][2] = "0";
				}
				i++;
			}
		} catch (Exception e) {
			logger.error("FCQueueReceiverManager] getMonitorData error", e);
			throw e;
		}
		return status;
	}

	public void resetCount(String receiverName) throws Exception {
		try {
			FCQueueReceiver receiverApp = (FCQueueReceiver) receivers.get(receiverName);
			if (receiverApp != null) {
				receiverApp.clearReceived();
			} else {
				throw new Exception("Receiver Already Stopped - " + receiverName);
			}

		} catch (Exception e) {
			logger.error("FCQueueReceiverManager] resetCount error", e);
			throw e;
		}
	}

	public String[] getAllReceiverNames() {
		String[] receiverName = new String[receivers.size()];
		Iterator<String> it = receivers.keySet().iterator();
		for (int i = 0; it.hasNext(); i++) {
			receiverName[i] = it.next();
		}
		return receiverName;
	}
}
