package com.eactive.eai.common.session; 

import java.util.function.Function;
import java.util.function.Supplier;

import org.apache.ignite.IgniteCache;

import com.eactive.eai.authserver.service.BearerTokenInfo;
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.message.EAIMessage;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.server.EAIServerVO;
import com.eactive.eai.common.sessioninfo.TerminalInfoVO;
import com.eactive.eai.common.util.Logger;
import com.openbanking.eai.common.token.AccessTokenVO;

/** 
 * 1. 기능 : 
 *     1) websocket login session 정보
 *     2) SA 응답거래 매핑 정보 
 *     3) Single Socket 접속정보
 *     4) 복원거래에 대한 헤더정보 저장
 *     정보 공유 관리하는 Manager 클래스
 * 2. 처리 개요 : Cache 를 통한 기능처리
 * 3. 주의사항 
 * 4. 오류처리
 *    deadlock 회피 - 2022.01.05 jun's
 *    1. WebSocketSessionManager.sessionClose() : synchronized 범위변경
 *    2. CustomRMICacheEventListener 
 *        - cache 가 다른 cache에 접근할때 Thread 방식으로 변경 
 *        - websocket 인스턴스에서만 작동하도록변경
 *    인스턴스 stop시 오류
 *    1. WebSocket 인증시에 HttpLogin 의  websocket instance및 어댑터 그룹 갱신
 *    2. sessionManager stop 시 expireThread interrupt 후 logout 처리  및  cacheManager.shutdown
 *    
 *
 * @author  : 
 * @version : v 1.0.0
 * @see : 관련 기능을 참조
 * @since   : 
 *  : 
 */
public abstract class SessionManager implements Lifecycle {
	static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
	
	public static final String ADAPTER_MASTER_CAHCE_NAME = "evictMasterCache";
	public static final String ADAPTER_CAHCE_NAME = "adapterSession";
	
	public static final String TOPIC_CAHCE_NAME = "topic";
	public static final String TOPIC_MAXSZIE = "topicMaxSize";
	public static final String CACHE_TOPIC_DURATION = "topicDurationSecs";
	
	public static final String CACHE_WORKING_DIR= "workingDir";
	public static final String STORE_CAHCE_NAME = "cacheStore";
	public static final String CAHCE_STORE_MAXSIZE = "cacheStoreMaxSize";
	public static final String CACHE_STORE_DURATION = "cacheStoreDurationSecs";
//	public static final String CACHE_MAX_MEM_MB = "cacheMaxMemMb";
	
	public static final String LOGIN_CAHCE_NAME = "loginSession";
	public static final String HTTP_CAHCE_NAME = "httpLogin";
	public static final String USERID_CAHCE_NAME = "convertUserIdSession";
	public static final String WEBSOCKER_CAHCE_NAME = "webSocketTimeout";
	public static final String TERMINAL_CAHCE_NAME = "terminalSession";
	public static final String CAGATEWAY_TOKEN_CACHE_NAME = "CAGatewayToken";
	public static final String OUTBOUND_ACCESS_TOKEN_CACHE_NAME = "OutBoundAccessToken";
	
	public static final String SESSION_MANAGER_GROUPNAME = "RMIInfo";
	public static final String SESSION_MANAGER_CACHETYPE = "cacheType";
	public static final String SESSION_MANAGER_CACHETYPE_IGNITE = "IGNITE";
	public static final String SESSION_MANAGER_CACHETYPE_EHCACHE = "EHCACHE";
	
	public static final String SESSION_MANAGER_EXPIRE_INTERVAL = "expireInterval";
	public static final String SESSION_MANAGER_MULTI_GROUP_IP = "multi_group_ip";
	public static final String LOGGER_LEVEL = "loggerLevel";
	public static final String SESSION_MANAGER_MULTI_GROUP_PORT = "multi_group_port";
	public static final String SESSION_MANAGER_SESSION_TIMEOUT = "sessionTimeout";
	public static final String SESSION_MANAGER_WEBSOCKET_TIMEOUT = "webSocketTimeout";

	public static final String SESSION_MANAGER_KUBERNETES_NAMESPACE     = "kubernetes_namespace";
	public static final String SESSION_MANAGER_KUBERNETES_SERVICENAME   = "kubernetes_servicename";
	public static final String SESSION_MANAGER_KUBERNETES_MASTERURL     = "kubernetes_masterurl";
	public static final String SESSION_MANAGER_KUBERNETES_ACCOUNTTOKEN  = "kubernetes_accounttoken";
	public static final String SESSION_MANAGER_KUBERNETES_DISCOVERYPORT = "kubernetes_discoveryport";

	public static final String ENABLE_MULTILOGIN_YN = "enable_multilogin_yn";
	public static final String SESSION_VO_VALIDATION_FROM = "session_vo_validation_from";
	   
	private static ChannelSessionFacade channelSessionFacade;
	private static SessionManager instance;

	private boolean started;

	public static final String MASTER_KEY = "master";

	private LifecycleSupport lifecycle = new LifecycleSupport(this);

	protected SessionManager() {
		// nothing to do
	}

	public static synchronized SessionManager getInstance() {
		if (instance == null) {
			PropManager manager = PropManager.getInstance();
			String type = manager.getProperty(SessionManager.SESSION_MANAGER_GROUPNAME,
					SessionManager.SESSION_MANAGER_CACHETYPE, SESSION_MANAGER_CACHETYPE_IGNITE);

			if (SESSION_MANAGER_CACHETYPE_IGNITE.equalsIgnoreCase(type)) {
				instance = new SessionManagerForIgnite();
			} else if (SESSION_MANAGER_CACHETYPE_EHCACHE.equalsIgnoreCase(type)) {
				instance = new SessionManagerForEhcache();
			} else {
				instance = new SessionManagerForIgnite();
			}
		}
		return instance;
	}

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

		lifecycle.fireLifecycleEvent(STARTING_EVENT, this);

		int port = 0;
		int remoteObjectPort = 0;
		
		channelSessionFacade = ChannelSessionFacade.getInstance();
		
		EAIServerManager mgr = EAIServerManager.getInstance();
		EAIServerVO vo = mgr.getEAIServer(mgr.getLocalServerName());
		// site의 포트 정책에 따라 변경될 수 있음.
		// EAIServerVO 에 정의됨.
		port = vo.getCacheLocalPort();
		remoteObjectPort = vo.getCacheRemotePort();
		init(port, remoteObjectPort);
		started = true;
		lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
	}

	public void stop() throws LifecycleException {
		if (!started) {
			throw new LifecycleException("RECEAICRT203");
		}
		lifecycle.fireLifecycleEvent(STOPING_EVENT, this);

		channelSessionFacade.onServerDown();

		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;
	}

	protected abstract void init(int localPort, int remoteObjectPort);

	// Single Socket 관련
	public abstract SessionVO getConnectionInfo(String key);

	public abstract SessionVO getConnectionStatus(String key);

	public abstract void putConnectionInfo(String key, SessionVO value);

	public abstract void removeConnectionInfo(String key);

	// Topic 관련
	public abstract EAIMessage getTopic(String key);

	public abstract void putTopic(String key, EAIMessage value);

	public abstract void removeTopic(String key);

	// 비동기 거래 복원용
	public abstract String getCacheStore(String key);

	public abstract void putCacheStore(String key, String value, int ttlSecs);
	
	public abstract void putCacheStore(String key, String value);

	public abstract void removeCacheStore(String key);

	// Login Cache
	public abstract SessionVO getLogin(String key);

	public abstract void putLogin(String key, SessionVO value);

	public abstract void removeLogin(String key);
	
	//CA Token Cache
	public abstract BearerTokenInfo getCAToken(String key);

	public abstract void putCAToken(String key, BearerTokenInfo value);

	public abstract void removeCAToken(String key);
	
	//Outbound Access Token Cache
	public abstract AccessTokenVO getOutboundAccessToken(String key, Function<AccessTokenVO, AccessTokenVO> getNewToken);

	public abstract void removeOutboundAccessToken(String key);
	
	public abstract void clearOutboundAccessToken();
	

	// HTTP Cache
	public abstract SessionVO getHttpLogin(String key);

	public abstract void putHttpLogin(String key, SessionVO value);

	public abstract void removeHTTPLogin(String key);

	// Terminal
	public abstract TerminalVO getTerminal(String key);

	public abstract void putTerminal(String key, TerminalVO value, TerminalInfoVO terminalInfoVO);

	public abstract void removeTerminal(String key);

	// CacheInfoVO
	public abstract CacheInfoVO getLoginCache();

	public abstract CacheInfoVO getHttpLoginCache();

	public abstract CacheInfoVO getEvictMasterCache();

	public abstract CacheInfoVO getTerminalCache();

	public abstract CacheInfoVO getConvertUserIdCache();

	public abstract CacheInfoVO getSocketCache();

	public abstract CacheInfoVO getOutboundAccessTokenCache();

	public abstract String getMasterInstId();

	// Convert terminalId to userId
	public abstract String getConvertUserId(String key);

	// WebSocketTimeout Cache
	public abstract void putWebSocketTimeout(String key, SessionVO value);

	public abstract void removeWebSocketTimeout(String key);

	public abstract void closeManager();
	
	public abstract String getCacheStatus();

}
