package com.eactive.eai.rms.onl.dashboard.repository;

import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.onl.tracking.vo.DbStaticsVo;
import com.eactive.eai.rms.onl.tracking.vo.MemStaticsVo;
import com.eactive.eai.rms.onl.vo.SmsVO;
import org.apache.commons.collections.map.MultiKeyMap;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;


@Scope(value = "singleton")
@Repository("dashboard2Repository")
public class Dashboard2Repository implements Serializable {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = -8968729820736492173L;

	/** The Constant logger. */
	private static final Logger logger = Logger.getLogger(Dashboard2Repository.class);

	// 서비스  ->  Map(EAI 서비스코드,서버구분코드)
	private HashMap<String,HashMap<String,String>> repoEAISevrDstcd;

	// 대시보드 거래건수 ( 메모리 버전 )
	private MultiKeyMap repoMemTransaction;

	// 서비스, 호스트 -> vo
	private MultiKeyMap repository;

	// 서비스 -> 호스트네임 리스트
	private HashMap<String,List<String>> repoHostNames;

	// 호스트명 -> 인스턴스네임 리스트
	private HashMap<String,List<String>> repoInstNames;

	// 호스트명 -> datasource, 인스턴스네임 list
	private HashMap<String, Map<String, List<String>>> repoDataSourceInstNames;

	// DB 에 저장된 통계정보 최대값.
	private String maxProcTime;


	// 호스트 telnet 연결 성공 여부 ( ip -> "1/0" )
	private HashMap<String,String> repoHostAlive;

	// SMS 메시지 리스트
	private List<SmsVO>  repoSms;

	// Peak Trade by TPS( service -> date,trade,tps )
	private HashMap<String,String> repoPeakTps;

	// Peak Trade by Trade ( service -> date,trade,tps )
	private HashMap<String,String> repoPeakTrade;


	// 일괄전송 전송현황 건수 ( 정상송신,정상수신,에러송신,에러수신,대기송신,대기수신)
	private int[] procBatch = new int[6];
	private int[] procEaiBatch = new int[6];

	//ITSM 대시보드 연동 거래처리 집계
	private HashMap<String,HashMap<String,String>> itsmDashTranInfo;

	//ITSM PEAK DAY 정보
	private HashMap<String,HashMap<String,String>> itsmDashPeakdayInfo;

	//ITSM 대시보드 연동 날짜별 거래건수
	private HashMap<String,List> itsmDashDayTranList;

	//ITSM 대시보드 연동 날짜별 거래건수
	private HashMap<String,List> itsmDashHourTranList;

	//ITSM 대시보드 연동 서비스별 업무에러 MAP
	private HashMap<String,HashMap<String,String>> itsmDashBizErrorMap;

	//ITSM 대시보드 연동 서비스별 타임아웃에러 MAP
	private HashMap<String,HashMap<String,String>> itsmDashTimeoutErrorMap;

	//ITSM 통신에러 MAP
	private HashMap<String,HashMap<String,String>> itsmDashConnectErrorMap;

	//ITSM 대시비도연동 서버 인스턴스별 거래건수
	private HashMap<String,HashMap<String,String>> itsmDashServerInstTotalProcMap;

	private String itsmDashTranInfoBatch;

	private String itsmDashTranInfoBatch2;

	private HashMap<String, HashMap<String, String>> itsmDashEaiBatchErrorList;

	private HashMap<String, HashMap<String, String>> itsmDashFepBatchErrorList;

	// 각 서비스별로 집계정보 저장
//	private Dashboard2ProcVo inTotalVo;
//	private Dashboard2ProcVo outTotalVo;
//	private Dashboard2ProcVo commTotalVo;
//	private Dashboard2ProcVo dmzTotalVo;
//	private Dashboard2ProcVo batchTotalVo;
//
//	private String lastTime; // 최종으로 저장된 startTime(모든 서비스에 공통)


	/**
	 * 저장소 를 초기화 한다.
	 */
	@PostConstruct
	public void createRepository() {
		repoEAISevrDstcd   = new HashMap<String,HashMap<String,String>>();
		repoMemTransaction = new MultiKeyMap();
		repository = new MultiKeyMap();
		repoHostNames = new HashMap<String,List<String>>();
		repoInstNames = new HashMap<String,List<String>>();
		repoDataSourceInstNames = new HashMap<String,Map<String,List<String>>>();
		repoHostAlive = new HashMap<String,String>();
		repoSms    = new ArrayList<SmsVO>();
		repoPeakTps   = new HashMap<String,String>();
		repoPeakTrade = new HashMap<String,String>();

		itsmDashTranInfo = new HashMap<String,HashMap<String,String>>();
		itsmDashPeakdayInfo = new HashMap<String,HashMap<String,String>>();
		itsmDashHourTranList = new HashMap<String,List>();
		itsmDashDayTranList = new HashMap<String,List>();
		itsmDashBizErrorMap = new HashMap<String,HashMap<String,String>>();
		itsmDashTimeoutErrorMap = new HashMap<String,HashMap<String,String>>();
		itsmDashConnectErrorMap = new HashMap<String,HashMap<String,String>>();
		itsmDashServerInstTotalProcMap  = new HashMap<String,HashMap<String,String>>();

		itsmDashEaiBatchErrorList = new HashMap<String,HashMap<String,String>>();
		itsmDashFepBatchErrorList = new HashMap<String,HashMap<String,String>>();

	}

	/**
	 * 저장소 데이터를 리셋 한다.
	 */
	public void initRepository() {
		repoEAISevrDstcd.clear();
		repoMemTransaction.clear();
		repository.clear();
		repoHostNames.clear();
		repoInstNames.clear();
		repoDataSourceInstNames.clear();
		repoHostAlive.clear();
		repoSms.clear();
		repoPeakTps.clear();
		repoPeakTrade.clear();

		for( int i = 0 ; i < 6 ; i++ ) {
			procBatch[i] = 0 ;
			procEaiBatch[i] = 0 ;
		}
	}

	public void initMemTrRepository() {
		repoMemTransaction.clear();
		repoMemTransaction = new MultiKeyMap();
	}

	public void checkMemTrRepository() {
		logger.info("check repoMemTransaction size=" + repoMemTransaction.size() );
	}

	public void removeMemTrRepository(String host, String inst) {
		repoMemTransaction.remove(host, inst);
	}

	/**
	 * 주어진 서비스의 DB 에서 읽어온 EAI서비스코드,서버구분코드 맵을 리턴한다.
	 * @param service
	 * @return
	 */
	public HashMap<String,String> getRepoEAIServrDstcd(String service) {
		return repoEAISevrDstcd.get(service);
	}

	/**
	 * 주어진 서비스명으로  메모리에서 읽어온 맵을 리턴한다.
	 * @param service
	 * @return
	 */
	public HashMap<String,String> getItsmDashTranInfo(String service) {
		return itsmDashTranInfo.get(service);
	}

	public HashMap<String,String> getItsmDashPeakdayInfo(String service) {
		return itsmDashPeakdayInfo.get(service);
	}


	/**
	 * 주어진 서비스명으로  메모리에서 읽어온 리스트을 리턴한다.
	 * @param service
	 * @return
	 */
	public List itsmDashHourTranList(String service) {
		return itsmDashHourTranList.get(service);
	}

	public List itsmDashDayTranList(String service) {
		return itsmDashDayTranList.get(service);
	}

	public HashMap<String, HashMap<String, String>> itsmDashBizErrorMap() {
		return itsmDashBizErrorMap;
	}

	public HashMap<String, HashMap<String, String>> itsmDashTimeoutErrorMap() {
		return itsmDashTimeoutErrorMap;
	}

	public HashMap<String, HashMap<String, String>> itsmDashConnectErrorMap() {
		return itsmDashConnectErrorMap;
	}

	public HashMap<String,String> itsmDashServerInstTotalProcMap(String service) {
		return itsmDashServerInstTotalProcMap.get(service);
	}

	public HashMap<String,HashMap<String,String>> itsmDashEaiBatchErrorList() {
		return itsmDashEaiBatchErrorList;
	}

	public HashMap<String,HashMap<String,String>> itsmDashFepBatchErrorList() {
		return itsmDashFepBatchErrorList;
	}

	/**
	 * 주어진 host 명이 포함된 DB 에서 읽어온 EAI서비스코드,서버구분코드 맵을 리턴한다.
	 * @param host
	 * @return
	 */
//	public HashMap<String,String> getRepoEAIServrDstcdByHost(String host) {
//		ServiceType service = getServiceTypeByHost(host);
//		if( service != null ) {
//			return getRepoEAIServrDstcd(service.getKey());
//		}
//
//		return null ;
//	}
//TODO JUN
	public HashMap<String,String> getRepoEAIServrDstcdByHost(String host) {
		DataSourceType dataSourceType = getServiceTypeByHost(host);
		if( dataSourceType != null ) {
			return getRepoEAIServrDstcd(dataSourceType.getName());
		}

		return null ;
	}

	/**
	 * 주어진 host 명이 포함된 DB 의 서비스 타입을 찾아서 리턴한다.
	 * @param host
	 * @return
	 */
//	public ServiceType getServiceTypeByHost(String host) {
//		Iterator<String> it = repoHostNames.keySet().iterator();
//		while( it.hasNext() ) {
//			String key = it.next();
//			List<String> list = repoHostNames.get(key);
//			for( String h : list ) {
//				if( h.equals(host) ) {
//					return ServiceType.get(key);
//				}
//			}
//		}
//
//		return null ;
//	}
//TODO JUN
	public DataSourceType getServiceTypeByHost(String host) {
		Iterator<String> it = repoHostNames.keySet().iterator();
		while( it.hasNext() ) {
			String key = it.next();
			List<String> list = repoHostNames.get(key);
			for( String h : list ) {
				if( h.equals(host) ) {
					return DataSourceTypeManager.getDataSourceType(key);
				}
			}
		}

		return null ;
	}
	/**
	 * Repository 에 주어진 서비스를 키로 하여 EAI서비스코드,서버구분코드 맵을 추가한다.
	 * @param service
	 * @param map
	 */
	public void setRepoEAISevrDstcd(String service, HashMap<String,String> map) {
		repoEAISevrDstcd.put(service, map);
	}

	/**
	 * Repository 에 주어진 서비스를 키로 하여 메모리 거래현황 맵을 추가한다.
	 * @param service
	 * @param map
	 */
	public void setItsmDashTranInfo(String service, HashMap<String,String> map) {
		itsmDashTranInfo.put(service, map);
	}

	public void setItsmDashPeakdayInfo(String service, HashMap<String,String> map) {
		itsmDashPeakdayInfo.put(service, map);
	}




	/**
	 * Repository 에 주어진 서비스를 키로 하여 메모리 시간별거래건수  맵을 추가한다.
	 * @param service
	 * @param map
	 */
	public void setItsmDashHourTranList(String service, List list) {

		itsmDashHourTranList.put(service, list);
	}


	/**
	 * Repository 에 주어진 서비스를 키로 하여 메모리 시간별거래건수  맵을 추가한다.
	 * @param service
	 * @param map
	 */
	public void setItsmDashDayTranList(String service, List list) {

		itsmDashDayTranList.put(service, list);
	}

	/**
	 * Repository 에 주어진 서비스를 키로 하여 메모리 업무에러리스트  맵을 추가한다.
	 * @param service
	 * @param map
	 */
	public void setItsmDashBizErrorMap(HashMap<String,HashMap<String, String>> map) {

		this.itsmDashBizErrorMap = map;
	}

	/**
	 * Repository 에 주어진 서비스를 키로 하여 메모리 타임아웃에러리스트  맵을 추가한다.
	 * @param service
	 * @param map
	 */
	public void setItsmDashTimeoutErrorMap(HashMap<String, HashMap<String, String>> map) {
		this.itsmDashTimeoutErrorMap = map;
	}

	public void setItsmDashConnectErrorMap(HashMap<String, HashMap<String, String>> map) {
		this.itsmDashConnectErrorMap = map;
	}


	public void setItsmDashServerInstTotalProcMap(String service ,HashMap<String,String> map) {

		this.itsmDashServerInstTotalProcMap.put(service, map);
	}


	public void setItsmDashTranInfoBatch( String batchCount ) {
		this.itsmDashTranInfoBatch = batchCount ;
	}

	public String getItsmDashTranInfoBatch() {
		return this.itsmDashTranInfoBatch;
	}

	public void setItsmDashTranInfoBatch2( String batchCount2 ) {
		this.itsmDashTranInfoBatch2 = batchCount2 ;
	}

	public String getItsmDashTranInfoBatch2() {
		return this.itsmDashTranInfoBatch2;
	}

	public void setItsmDashEaiBatchErrorList(HashMap<String,HashMap<String,String>> eaiBatchErrorList) {
		this.itsmDashEaiBatchErrorList = eaiBatchErrorList;
	}

	public void setItsmDashFepBatchErrorList(HashMap<String,HashMap<String,String>> fepBatchErrorList) {
		this.itsmDashFepBatchErrorList = fepBatchErrorList;
	}

	/**
	 * 메모리 건래건수 Repository 를 리턴한다.
	 * @return
	 */
	public MultiKeyMap getRepoMemTr() {
		return repoMemTransaction;
	}


	/**
	 * 저장소 객체를 리턴한다.
	 * @return
	 */
	public MultiKeyMap getRepository() {
		return repository;
	}

	/**
	 * 서비스 -> 호스트명 리스트를 저장한다.
	 * @param service
	 * @param list
	 */
	public void setHostNames(String service, List<String> list ) {
		repoHostNames.put(service,list);
	}

	/**
	 * 호스트명 -> 인스턴스 리스트를 저장한다.
	 * @param host
	 * @param list
	 */
	public void setInstNames(String host, List<String> list ) {
		repoInstNames.put(host,list);
	}

	public void setDataSourceInstNames(String host, Map<String, List<String>> putData ) {
		repoDataSourceInstNames.put(host, putData);
	}

	/**
	 * Memory 거래현황정보 추가
	 * @param host
	 * @param inst
	 * @param vo
	 */
	//대시보드 메모리 set synchronized 추가테스트 20201019
	public synchronized void setMemTradeVo( String host, String inst, MemStaticsVo vo) {
		repoMemTransaction.put(host,inst,vo);
	}

	/**
	 * 메모리 거래현황 Repository 에서 주어진 호스트명, 인스턴스명 에 대한 Vo 객체를 찾아서 리턴한다.
	 * @param host
	 * @param inst
	 * @return
	 */
	//대시보드 메모리 get synchronized 추가테스트 20201019
	public synchronized MemStaticsVo getMemTradeVo(String host, String inst) {
		MemStaticsVo vo = (MemStaticsVo)repoMemTransaction.get(host,inst);
		if( vo == null ) {
			vo = new MemStaticsVo();
			vo.setHostName(host);
			vo.setInstName(inst);
		}

		return vo;
	}

	/**
	 * 주어진 호스트명 에서 수신된 Vo 리스트를 찾아서 리턴한다.
	 * @param host
	 * @return
	 */
	public List<MemStaticsVo> getMemTradeVoByHost( String host ) {
		List<MemStaticsVo> list = new ArrayList<MemStaticsVo>();
		List<String> insts = getInstNames(host);

		if( insts != null ) {
			for( String inst : insts ) {
				list.add( getMemTradeVo(host,inst) );
			}
		}

		return list;
	}

	/**
	 * 주어진 서비스에서 수신된 전체 Vo 리스트를 찾아서 리턴한다.
	 * @param service
	 * @return
	 */
	public List<MemStaticsVo> getMemTradeVoByService( String service ) {
		List<MemStaticsVo> list = new ArrayList<MemStaticsVo>();
		List<String> hosts = getHostNames(service);
		if( hosts != null ) {
			for( String host : hosts ) {
				list.addAll( getMemTradeVoByHost(host) );
			}
		}

		return list;
	}

	/**
	 * 주어진 서비스에서 수신된 전체 Vo 리스트를 찾아서 리턴한다.
	 * repoDataSourceInstNames 에서 host, instance 명 조회 후 MemTradeVo를 리턴한다.
	 * @param service
	 * @return
	 */
	public List<MemStaticsVo> getDashboardMemTradeVoByService(String service) {
		return Optional.ofNullable(getHostNames(service))
				.orElse(Collections.emptyList())
				.stream()
				.flatMap(host -> getInstListByHostAndService(host, service).stream()
						.map(instNm -> getMemTradeVo(host, instNm)))
				.collect(Collectors.toList());
	}

	private List<String> getInstListByHostAndService(String host, String service) {
		return Optional.ofNullable(repoDataSourceInstNames)
				.map(m -> m.get(host))
				.map(serviceMap -> serviceMap.get(service))
				.orElse(Collections.emptyList());
	}


	public void setTradeVo(String service, String host, DbStaticsVo vo) {
		repository.put(service, host, vo);
	}

	public DbStaticsVo getTradeVo(String service, String host) {

		DbStaticsVo vo = (DbStaticsVo)repository.get(service, host);
		if( vo == null ) {
			vo = new DbStaticsVo();
			vo.init();
		}

		return vo;
	}

	public List<DbStaticsVo> getTradeVo(String service) {
		List<DbStaticsVo> list = new ArrayList<DbStaticsVo>();

		List<String> hostNames = repoHostNames.get(service);
		if( hostNames != null ) {
			for( String host : hostNames ) {
				list.add( getTradeVo(service, host) );
			}
		}

		if( list.size() == 0 ) {
			DbStaticsVo vo = new DbStaticsVo();
			vo.init();
			list.add(vo);
		}

		return list;
	}

	public DbStaticsVo getTotalVo(String service) {
		List<DbStaticsVo> list = getTradeVo(service);

		DbStaticsVo retVo = new DbStaticsVo();
		retVo.init();
		for( DbStaticsVo vo : list ) {
			retVo.add( vo );
		}

		return retVo ;
	}


	public void setMaxProcTime( String fulltime ) {
		this.maxProcTime = fulltime ;
	}

	public String getMaxProcTime() {
		return this.maxProcTime;
	}


	public boolean isInitHostRepository() {
		if( repoHostAlive.size() > 0 )
			return true ;
		else
			return false ;
	}

	public void addHostKey( List<String> listAddr ) {

		if( listAddr != null ) {
			for( String addr : listAddr ) {
				repoHostAlive.put(addr, "1");
			}
		}
	}

	public boolean isHostAlive( String ipaddr, String port ) {
		String alive = repoHostAlive.get(ipaddr + ":" + port);

		// 디폴트는 성공으로 간주하고, 실패시에만 Fail 처리한다.
		if( "0".equals(alive) ) {
			return false ;
		} else {
			return true ;
		}
	}

	public boolean isHostAlive( String ipaddr ) {
		boolean foundDead = false ;

		// ip:port
		Iterator it = repoHostAlive.keySet().iterator();
		while( it.hasNext() ) {
			String key = (String)it.next();
			String tmp[] = key.split(":");
			if( tmp.length > 1 ) {
				if( ipaddr.equals( tmp[0] ) ) {
					String alive = repoHostAlive.get(key);
					if( "1".equals(alive) ) {
						return true ;
					} else {
						foundDead = true ;
					}
				}
			}
		}

		if( foundDead ) {
			// 모든 inst 가 실패
			return false ;
		}

		// 디폴트
		return true ;
	}

	public void setHostAlive( String ipaddr, String port,  boolean alive ) {
		String aliveVal = "1" ;
		if( alive == false ) {
			aliveVal = "0" ;
		}

		repoHostAlive.put(ipaddr + ":" + port, aliveVal );
	}

	/**
	 * ip : port 형태의 주소값 리스트를 리턴한다.
	 * @return
	 */
	public List<String> getHostIPList() {
		Iterator it = repoHostAlive.keySet().iterator();

		List<String> list = new ArrayList<String>();
		while( it.hasNext() ) {
			String key = (String)it.next();
			list.add( key );
		}

		return list ;
	}

	public List<String> getHostNames( String service ) {
		return repoHostNames.get(service);
	}

	public List<String> getInstNames( String host ) {
		return repoInstNames.get(host);
	}

	public void addSms( SmsVO vo ) {
		repoSms.add( vo );

		// 만일 100개가 넘으면 앞에서 부터 50개를 삭제한다.
		if( repoSms.size() >= 100 ) {
		}

	}

	public void setPeakTps( String service, String val ) {
		repoPeakTps.put(service, val);
	}

	public String getPeakTps( String service ) {
		return repoPeakTps.get(service);
	}

	public void setPeakTrade( String service, String val ) {
		repoPeakTrade.put(service, val);
	}

	public String getPeakTrade( String service ) {
		return repoPeakTrade.get(service);
	}

	public String getProcBatch( ) {
		StringBuffer sb = new StringBuffer();
		for( int i = 0 ; i < this.procBatch.length ; i++ ) {
			sb.append(this.procBatch[i]).append(",");
		}
		return sb.toString();
	}

	public String getProcEaiBatch( ) {
		StringBuffer sb = new StringBuffer();
		for( int i = 0 ; i < this.procEaiBatch.length ; i++ ) {
			sb.append(this.procEaiBatch[i]).append(",");
		}
		return sb.toString();
	}

	public void setProcBatch( int p1, int p2, int p3, int p4, int p5, int p6 ) {
		this.procBatch[0] = p1 ;
		this.procBatch[1] = p2 ;
		this.procBatch[2] = p3 ;
		this.procBatch[3] = p4 ;
		this.procBatch[4] = p5 ;
		this.procBatch[5] = p6 ;
	}

	public void setProcEaiBatch( int p1, int p2, int p3, int p4, int p5, int p6 ) {
		this.procEaiBatch[0] = p1 ;
		this.procEaiBatch[1] = p2 ;
		this.procEaiBatch[2] = p3 ;
		this.procEaiBatch[3] = p4 ;
		this.procEaiBatch[4] = p5 ;
		this.procEaiBatch[5] = p6 ;
	}

	// 주어진 데이터소스명을 기준으로 hostname list를 리턴한다.
	public List<Map<String,String>> getDatasourceByHost(String datasourcName){
		List<String> hostList = repoHostNames.get(datasourcName);
		List<Map<String,String>> serverInfoList = new ArrayList<Map<String,String>>();

		if( hostList != null ){
			for(String hostName : hostList) {
				Map<String, List<String>> dataSourceInst = repoDataSourceInstNames.get(hostName);
				if( dataSourceInst != null ){
					List<String> instNameList = dataSourceInst.get(datasourcName);

					if( instNameList != null ){
						for (String instName : instNameList) {
							Map<String, String> serverInfo = new HashMap<String, String>();

							serverInfo.put("HOSTNAME", hostName);
							serverInfo.put("EAISVRINSTNM", instName);

							serverInfoList.add(serverInfo);
						}
					}
				}
			}
		}

		return serverInfoList;
	}

	// 주어진 host 명을 기준으로 map datasource, instance list 를 리턴한다.
	public Map<String, List<String>> getDatasourceInstListByHostNm(String hostName){
		return repoDataSourceInstNames.getOrDefault(hostName, new HashMap<String, List<String>>());
	}

}
