package com.eactive.eai.adapter.http.healthcheck;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
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.util.Logger;

public class HttpTargetMonitorManager implements Lifecycle {
	private static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
	
	public static final String threadName = "HttpTargetMonitorManager";
	
	public static final String PROP_GROUP = "HttpTargetMonitor";
	public static final String CHECK_INTERVAL_SECS = "check.interval.secs";
	private static final int CHECK_INTERVAL_MINSECS = 30;
	
	private int checkIntervalSecs = 60;
	
	private static HttpTargetMonitorManager manager = new HttpTargetMonitorManager();

	private LifecycleSupport lifecycle = new LifecycleSupport(this);

	private boolean started;

	private ConcurrentHashMap<String, HttpHealthChecker> checkers = new ConcurrentHashMap<>();

	private HttpTargetMonitor runner;

	private Timer timer;

	public static HttpTargetMonitorManager getInstance() {
		return manager;
	}
	
	private void loadHttpAdapterList() {
		AdapterManager adapterManager = AdapterManager.getInstance();
		
		HashMap<String, AdapterGroupVO> adapterGroups = adapterManager.getAllAdapterGroupVO();
		for(AdapterGroupVO adapterGroup : adapterGroups.values()) {			
			if( adapterGroup.isUsedFlag() 
			 && ( StringUtils.equals(Keys.ADAPTER_OUT, adapterGroup.getGubun()) )
			 && ( StringUtils.equals(Keys.TYPE_HTTP, adapterGroup.getType()) || StringUtils.equals(Keys.TYPE_REST, adapterGroup.getType()))
			 ) {
				addAdapterGroup(adapterGroup);
			}
		}
	}
	
	public void addAdapterGroup(AdapterGroupVO adapterGroup) {
		AdapterPropManager propManager = AdapterPropManager.getInstance();
		Iterator<AdapterVO> adapters = adapterGroup.getAdapters();
		while(adapters.hasNext()) {
			AdapterVO adapter = adapters.next();
			String healthCheckUrl = propManager.getProperty(adapter.getPropGroupName(), HttpClientAdapterServiceKey.HEALTHCHECK_URL);
			String timeoutMs = propManager.getProperty(adapter.getPropGroupName(), HttpClientAdapterServiceKey.HEALTHCHECK_TIMEOUTMS);
			
			if(StringUtils.isNotBlank(healthCheckUrl)) {				
				if(adapterGroup.isUsedFlag()) {
					if(logger.isInfo()) {
						logger.info(String.format("loadHttpAdapterList AdapterName=%s, healthCheckUrl=%s timeoutMs=%s", adapter.getName(), healthCheckUrl, timeoutMs));
					}
					addHttpHeacthChecker(healthCheckUrl, timeoutMs, adapter);					
				}
				else {
					if(logger.isInfo()) {
						logger.info(String.format("removeHttpHeacthChecker AdapterName=%s, healthCheckUrl=%s timeoutMs=%s", adapter.getName(), healthCheckUrl, timeoutMs));
					}
					removeHttpHeacthChecker(adapter);
				}
			}
			else {
				if(logger.isInfo()) {
					logger.info(String.format("removeHttpHeacthChecker AdapterName=%s, healthCheckUrl=%s timeoutMs=%s", adapter.getName(), healthCheckUrl, timeoutMs));
				}
				removeHttpHeacthChecker(adapter);
			}
		}
	}
	
	public void start() throws LifecycleException {
		logger.error(threadName +"] is start");
		if (started)
			throw new LifecycleException("RECEAICTM001");

		lifecycle.fireLifecycleEvent(STARTING_EVENT, this);
		
		loadHttpAdapterList();
		
		PropManager pmanager = PropManager.getInstance();
		// default 1 mins
		
		try {
			checkIntervalSecs = Integer.parseInt(pmanager.getProperty(PROP_GROUP, CHECK_INTERVAL_SECS));
		} catch (NumberFormatException e) {
			logger.warn( String.format("%s %s not number", PROP_GROUP, CHECK_INTERVAL_SECS), e);
		}
		
		if(checkIntervalSecs < CHECK_INTERVAL_MINSECS) {
			logger.warn( String.format("%s %s too small, use default %s", CHECK_INTERVAL_SECS, checkIntervalSecs, CHECK_INTERVAL_MINSECS));
			checkIntervalSecs = CHECK_INTERVAL_MINSECS;
		}
		
		this.runner = new HttpTargetMonitor();
		this.runner.setManager(this);

		this.timer = new Timer(threadName);
		
		long period = (checkIntervalSecs * 1000);
		this.timer.schedule(this.runner, period, period);

		started = true;
		lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
	}

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

		lifecycle.fireLifecycleEvent(STOPING_EVENT, this);

		this.timer.cancel();
		this.timer = null;
		this.runner = null;

		if (this.checkers != null) {
			this.checkers.clear();
			this.checkers = null;
		}

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

	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 void addHttpHeacthChecker(String healthCheckUrl, String timeoutMsVal, AdapterVO vo) {
		HttpHealthChecker checker = new HttpHealthChecker();
		checker.setHealthCheckUrl(healthCheckUrl);
		
		int timeoutMs = 2000;
		if(StringUtils.isNoneBlank(timeoutMsVal)) {
			try {
				timeoutMs = Integer.parseInt(timeoutMsVal);
			}
			catch(NumberFormatException ne) {
				timeoutMs = 2000;
			}
		}
		
		if(checkIntervalSecs <= timeoutMs / 1000) {
			logger.warn( String.format("%s timeoutMs %s too big, use default %s", vo.getName(), timeoutMs, (checkIntervalSecs-1) * 1000));
			timeoutMs = (checkIntervalSecs-1) * 1000;
		}
		
		checker.setTimeoutMillis(timeoutMs);
		checker.setAdapter(vo);
		this.checkers.put(vo.getAdapterGroupName() + "-" + vo.getName(), checker);
	}
	
	public void removeHttpHeacthChecker(AdapterVO vo) {
		this.checkers.remove(vo.getAdapterGroupName() + "-" + vo.getName());
	}
	
	public Iterator<String> getAllAdapterNames() {
		HashMap<String, HttpHealthChecker> map = new HashMap<>(checkers);
		return map.keySet().iterator();
	}
	
	public String[] getAllAdapterNamesWithGroupName(String adapterGroupName) {
		HashMap<String, HttpHealthChecker> map = new HashMap<>(checkers);
		String namePrefix = adapterGroupName + "-";
		Iterator<String> it = map.keySet().iterator();
		List<String> list = new ArrayList<>();
		while(it.hasNext()) {
			String key = it.next();
			if(key.startsWith(namePrefix)) {
				list.add(key);
			}
		}
		return list.toArray(new String[0]);
	}
	
	public HttpHealthChecker getHttpHeacthChecker(String adapterName) {
		return this.checkers.get(adapterName);
	}

	public void removeHttpHeacthChecker(String adapterName) {
		this.checkers.remove(adapterName);
	}
	
	public void removeHttpHeacthCheckerWithGroupName(String adapterGroupName) {
		if(logger.isInfo()) {
			logger.info("removeHttpHeacthCheckerWithGroupName - " +adapterGroupName);
		}
		String[] adatperNames = getAllAdapterNamesWithGroupName(adapterGroupName);
		for(int i=0; i< adatperNames.length; i++) {
			if(logger.isInfo()) {
				logger.info("removeHttpHeacthCheckerWithGroupName remove - " +adatperNames[i]);
			}
			this.checkers.remove(adatperNames[i]);
		}
	}
	
	public int getCheckerCount() {
		return this.checkers.size();
	}

}
