package com.eactive.eai.common.testcall;

import java.util.Iterator;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;

import com.eactive.eai.adapter.AdapterVO;
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.util.Logger;

public class TestCallManager implements Lifecycle {
	private static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

	public static final String PROP_GROUP = "TestCallManager";

	// TestCallManager에서 사용하는 Property Key : TestCallRunner의 동작 주기
	public static final String PROP_PERIOD = "testcall.runner.period";

	// TestCallManager에서 사용하는 Property Key : TestCallRunner의 응답 타입
	public static final String RESPONSE_TYPE = "RESPONSE_TYPE";

	// TestCallManager에서 사용하는 Property Key : TestCallRunner의 HTTP 응답 타임 아웃
	public static final String HTTP_TIME_OUT = "HTTP_TIME_OUT";

	// TestCallManager에서 사용하는 Property Key : TestCallRunner의 HTTP 접속 타임 아웃
	public static final String CONNECTION_TIMEOUT = "CONNECTION_TIMEOUT";

	/**
	 * TestCallManager에서 사용하는 Property Key : TestCallRunner의 TESTCALL 여부 "Y" 이면 테스트콜
	 * 거래 로그 안찍힘 "N" 면 테스트콜 거래 로그 찍힘
	 */
	public static final String TESTCALL_YN = "TESTCALL_YN";

	// TestCallManager에서 사용하는 Property Key : TestCallRunner의 요청 메시지
	public static final String REQUEST_MESSAGE = "REQUEST_MESSAGE";

	// TestCallManager에서 사용하는 Property Key : TestCallRunner의 응답 메시지
	public static final String RESPONSE_MESSAGE = "RESPONSE_MESSAGE";

	private static TestCallManager manager = new TestCallManager();

	private LifecycleSupport lifecycle = new LifecycleSupport(this);

	private boolean started;

	private ConcurrentHashMap<String, TestCall> testers = new ConcurrentHashMap<>();

	private TestCallRunner runner;

	private Timer timer;

	public static TestCallManager getInstance() {
		return manager;
	}

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

		lifecycle.fireLifecycleEvent(STARTING_EVENT, this);

		PropManager pmanager = PropManager.getInstance();
		// default 10 mins
		long period = 1000 * 600;
		try {
			period = Long.parseLong(pmanager.getProperty(PROP_GROUP, PROP_PERIOD));
		} catch (Exception e) {
			logger.info("PROP_PERIOD not number");
		}

		this.runner = new TestCallRunner();
		this.runner.setTestCallManager(this);

		this.timer = new Timer("TestCallTimer");
		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.testers != null) {
			this.testers.clear();
			this.testers = 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 addTester(AdapterVO vo) throws TestCallException {
		if (vo.getTestCallClass() == null)
			throw new TestCallException("RECEAICTM005");
		Class<?> tclass = null;

		if (!started) {
			logger.error("TesterManager] TesterManager is stopped");
		}

		try {
			tclass = Class.forName(vo.getTestCallClass());
		} catch (Exception e) {
			logger.error("TesterManager] addTester Error. [" + vo.getAdapterGroupName() + "][" + vo.getName() + "]["
					+ vo.getTestCallClass() + "]", e);
			throw new TestCallException(ExceptionUtil.getErrorCode(e, "RECEAICTM003"), e);
		}

		try {
			TestCall tester = (TestCall) tclass.newInstance();
			tester.setAdapterInfo(vo);
			if (this.testers != null && vo != null) {
				this.testers.put(vo.getAdapterGroupName() + "-" + vo.getName(), tester);
			}
		} catch (Exception e) {
			logger.error("TesterManager] addTester Error. [" + vo.getAdapterGroupName() + "][" + vo.getName() + "]["
					+ vo.getTestCallClass() + "]", e);
			throw new TestCallException(ExceptionUtil.getErrorCode(e, "RECEAICTM004"));
		}
	}

	public Iterator<String> getAllAdapterNames() {
		ConcurrentHashMap<String, TestCall> map = new ConcurrentHashMap<>(testers);
		return map.keySet().iterator();
	}

	public TestCall getTestCall(String adapterName) {
		return this.testers.get(adapterName);
	}

	public void removeTestCall(String adapterName) {
		this.testers.remove(adapterName);
	}

	public int getTestCallCount() {
		return this.testers.size();
	}
}
