package com.eactive.eai.common.stdmessage;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

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.util.ApplicationContextProvider;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.ObjectUtil;
import com.eactive.eai.message.StandardMessage;

@Component
public class STDMessageManager implements Lifecycle {
	static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

	@Autowired
	STDMessageDAO sTDMessageDAO;

	private HashMap<String, StandardMessage> headers = new HashMap<>();
	private HashMap<String, StandardMessage> headersForPathVariables = new HashMap<>();
	private HashMap<String, STDMsgInfoAddOnVO> headerUrls = new HashMap<>();
	private HashMap<String, STDMsgInfoAddOnVO> headerUrlsForPathVariables = new HashMap<>();

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

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

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

		lifecycle.fireLifecycleEvent(STARTING_EVENT, this);
		try {
			init();
		} catch (Exception e) {
			throw new LifecycleException(ExceptionUtil.getErrorCode(e, "RECEAICKE202"));
		}
		started = true;
		lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
	}

	private void init() throws Exception {
		headers = sTDMessageDAO.getAllSTDMessages();
		headersForPathVariables.clear();
		for (Map.Entry<String, StandardMessage> entry : headers.entrySet()) {
			if (isPathVariable(entry.getKey())) {
				headersForPathVariables.put(entry.getKey(), entry.getValue());
			}
		}
		// url base로 처리 추가.
		headerUrls = sTDMessageDAO.getAllStdMsgInfos();
		headerUrlsForPathVariables.clear();
		for (Map.Entry<String, STDMsgInfoAddOnVO> entry : headerUrls.entrySet()) {
			if (isPathVariable(entry.getKey())) {
				headerUrlsForPathVariables.put(entry.getKey(), entry.getValue());
			}
		}
	}

	public static boolean isPathVariable(String key) {
		return StringUtils.containsAny(key, '*', '{', '?');
	}

	public synchronized void reload() throws Exception {
		if (logger.isWarn()) {
			logger.warn("STDMessageManager] reload all Started ...");
		}
		init();
		if (logger.isWarn()) {
			logger.warn("STDMessageManager] reload all finished ...");
		}
	}

	public synchronized void reload(String keyName) throws Exception {
		StandardMessage msg = sTDMessageDAO.getSTDMessage(keyName);
		if (msg != null) {
			headers.put(keyName, msg);
			if (isPathVariable(keyName)) {
				headersForPathVariables.put(keyName, msg);
			}
		} else {
			throw new Exception("STDMessage not found in Database : key[" + keyName + "]");
		}
		// url base로 처리 추가.
		String apiFullPath = sTDMessageDAO.getApiFullPath(keyName);
		STDMsgInfoAddOnVO msgUrl = sTDMessageDAO.getUrlStdMsgInfo(apiFullPath);
		if (msgUrl != null) {
			headerUrls.put(apiFullPath, msgUrl);
			if (isPathVariable(apiFullPath)) {
				headerUrlsForPathVariables.put(apiFullPath, msgUrl);
			}
		} else {
			throw new Exception("STDMessage not found in Database : key[" + apiFullPath + "]");
		}
	}

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

		lifecycle.fireLifecycleEvent(STOPING_EVENT, this);

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

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

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

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

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

	public StandardMessage getSTDMessage(String bwkSvcKey) {
		StandardMessage stdmessage = (StandardMessage) headers.get(bwkSvcKey);
		try {
			if (stdmessage != null) {
				stdmessage = (StandardMessage) stdmessage.clone();
			}
			return stdmessage;
		} catch (Exception e) {
			throw new RuntimeException(ExceptionUtil.getErrorCode(e, "RECEAIEMM204"));
		}
	}

	public StandardMessage getSTDMessageForPathVariable(String bwkSvcKey) {
		String matchedKey = getMatchedPathVariable(bwkSvcKey);
		if (matchedKey != null) {
			return getSTDMessage(matchedKey);
		} else {
			return null;
		}
	}

	public String getMatchedPathVariable(String bwkSvcKey) {
		AntPathMatcher matcher = new AntPathMatcher();
		for (String key : headersForPathVariables.keySet()) {
			if (matcher.match(key, bwkSvcKey)) {
				return key;
			}
		}
		return null;
	}

	// apiFullPath 기준 Path Variable 조회
	public StandardMessage getSTDMessageForUrlPathVariable(String apiFullPath) {
		String matchedKey = getMatchedUrlPathVariable(apiFullPath);
		if (matchedKey != null) {
			return getSTDMessage(matchedKey);
		} else {
			return null;
		}
	}

	public String getMatchedUrlPathVariable(String apiFullPath) {
		AntPathMatcher matcher = new AntPathMatcher();
		for (String key : headerUrlsForPathVariables.keySet()) {
			if (matcher.match(key, apiFullPath)) {
				return key;
			}
		}
		return null;
	}
	
	public STDMsgInfoAddOnVO getStdMsgInfoAddOn(String apiFullPath) {
		for (Map.Entry<String, STDMsgInfoAddOnVO> entry : headerUrls.entrySet()) {
			if (StringUtils.equals(entry.getKey(), apiFullPath) ) {
				return entry.getValue();
			}
		}
		String apiPathVariable = getMatchedUrlPathVariable(apiFullPath);
		for (Map.Entry<String, STDMsgInfoAddOnVO> entry : headerUrlsForPathVariables.entrySet()) {
			if (StringUtils.equals(entry.getKey(), apiPathVariable) ) {
				return entry.getValue();
			}
		}
		return null;
	}

	public String[] getAllSTDMessageKeys() {
		String[] ks = new String[this.headers.size()];
		Iterator<String> it = this.headers.keySet().iterator();
		for (int i = 0; it.hasNext(); i++) {
			ks[i] = (String) it.next();
		}
		Arrays.sort(ks);
		return ks;
	}

	public ArrayList<StandardMessage> getAllSTDMessages() {
		ArrayList<StandardMessage> al = new ArrayList<>();
		Iterator<StandardMessage> it = this.headers.values().iterator();
		while (it.hasNext()) {
			al.add(it.next());
		}
		return al;
	}
	
	// 업무서비스키로 기 동록된 apiFullPath 가져오기.
	public String getApiFullPath(String serviceKey) {
		String apiFullPath = "";
		for (Map.Entry<String, STDMsgInfoAddOnVO> entry : headerUrls.entrySet()) {
			STDMsgInfoAddOnVO msgVO = entry.getValue();
			if (StringUtils.equals(serviceKey, msgVO.getBzwksvckeyname())) {
				apiFullPath = msgVO.getApiFullPath();
				return apiFullPath;
			}
		}
		return null;
	}

	public void addSTDMessage(StandardMessage msg) {
		this.headers.put(msg.getServiceKey(), msg);

		if (isPathVariable(msg.getServiceKey())) {
			headersForPathVariables.put(msg.getServiceKey(), msg);
		}
	}

	public synchronized void removeSTDMessage(String bwkSvcKey) {
		this.headers.remove(bwkSvcKey);
		this.headersForPathVariables.remove(bwkSvcKey);
		// apiFullPath 기준 등록정보 삭제.
		String apiFullPath = getApiFullPath(bwkSvcKey);
		this.headerUrls.remove(apiFullPath);
		this.headerUrlsForPathVariables.remove(apiFullPath);
	}
}
