package com.eactive.eai.rms.onl.common.service;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import org.apache.log4j.Logger;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.springframework.context.ApplicationContext;

import com.eactive.eai.adapter.http.client.HttpClientAdapterMain;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.util.CommonUtil;
import com.eactive.eai.rms.data.entity.onl.adapter.AdapterGroupService;
import com.eactive.eai.rms.onl.server.repository.AdaptersStatusRepository;
import com.eactive.eai.rms.onl.server.repository.Repository;
import com.eactive.eai.rms.onl.vo.AdapterStatusVO;
import com.eactive.eai.rms.onl.vo.AdaptersStatusVO;

public class AdaptersStatusSendJob implements Job {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(AdaptersStatusSendJob.class);

	private Repository repository;

	private AdaptersStatusRepository adapterStatusRepository;

	private MonitoringContext monitoringContext;

	private EaiServerInfoService eaiServerInfoService;

	private AdapterGroupService adapterGroupService;

	private HashMap<String, String> replace;
	private ArrayList<String> block;
	private boolean isLoad;
	private AdapterStatusSendThreadPool executorServicePool;
	private List<Map<String, String>> ipList;
	private ArrayList<String> adapterList;

	public AdaptersStatusSendJob() {
		executorServicePool = (AdapterStatusSendThreadPool) AdapterStatusSendThreadPool.getInstance();
		replace = new HashMap<String, String>();
		block = new ArrayList<String>();
		ipList = new ArrayList<Map<String, String>>();
		isLoad = true;
	}

	public synchronized void init(){
		if (logger.isInfoEnabled()){
			logger.info("Adapters Status Send init start ");
		}    	
    	replace = new HashMap<String,String>();
    	block   = new ArrayList<String>();
    	Properties prop = monitoringContext.getProperties();
    	for (Object o :prop.keySet()){
        	String key = (String)o;
        	//replace 목룍 메모리에 저장
        	if (key.startsWith("AdapterStatusReplace")){
	        	String value = prop.getProperty(key);
	        	if(value != null){
	        		String [] data = value.split("\\|");
		        	if (data.length == 2) {
		        		replace.put(data[0], data[1]);
		        	}
	        	}
	        	
        	}
        	//전송 제거 목록 메모리에 저장
        	if (key.startsWith("AdapterStatusBlock")){
	        	String value = prop.getProperty(key);
        		block.add(value);
        	}
        }
    	
    	List<DataSourceType> soruceTypes = DataSourceTypeManager.getOnlineDataSourceTypes();
    	ArrayList<String> allAdapterList = new ArrayList<>();
    	ArrayList<Map<String, String>> allIpList = new ArrayList<>();
    	for (DataSourceType dt : soruceTypes){
    		DataSourceContextHolder.setDataSourceType(dt);
			List<String> alAdapterList = adapterGroupService.findAll().stream()
					.map(a -> a.getAdptrbzwkgroupname()).collect(Collectors.toList());
			for (int i = 0; i < alAdapterList.size(); i++) {
				if (logger.isInfoEnabled()) {
					logger.info("AdaptersStatusSendScheduler] Adapters List=" + alAdapterList.get(i));
				}
				allAdapterList.add(alAdapterList.get(i));
			}
    		
   			List<Map<String, String>> alIpList = eaiServerInfoService.getEaiServerIpList();
    		for(int i=0;i < alIpList.size();i++){
    			if (logger.isInfoEnabled()){
    				logger.info("AdaptersStatusSendScheduler] IP List="+alIpList.get(i));
    			}        			
    			allIpList.add(alIpList.get(i));
    		}
   			
    	}
    	this.adapterList = allAdapterList;
    	this.ipList = allIpList;
    	isLoad = false;
		if (logger.isInfoEnabled()){
			logger.info("Adapters Status Send init end ");
		}    	
    }

	public void setLoad(boolean load) {
		this.isLoad = load;
	}

	public boolean isLoad() {
		return this.isLoad;
	}

	public ArrayList selectData() {
		ArrayList<String> al = new ArrayList<String>();
		for (String key : replace.keySet()) {
			al.add(key + "|" + replace.get(key));
		}
		al.addAll(block);
		return al;
	}

	// @ScheduleMethod(type = ScheduleType.CRON, cronExpression = "0/10 * * * * ?")
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		// JobKey jobKey = context.getJobDetail().getKey();

		ApplicationContext appContext = null;
		try {
			appContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
		} catch (SchedulerException e) {
			logger.error("applicationContext get module error", e);
			return;
		}

		repository = (Repository) appContext.getBean("repository");
		adapterStatusRepository = (AdaptersStatusRepository) appContext.getBean("adaptersStatusRepository");
		monitoringContext = appContext.getBean(MonitoringContext.class);
		eaiServerInfoService = (EaiServerInfoService) appContext.getBean("eaiServerInfoService");
		adapterGroupService = appContext.getBean(AdapterGroupService.class);
		run();
	}

	public void run() {
		if (monitoringContext == null)
			return;
		if (monitoringContext != null && isLoad) {
			init();
		}

		if (logger.isInfoEnabled()) {
			logger.info("Adapters Status Send start ");
		}
		// 1.adapter 를 인스턴스구별 없이 통합
		getInstanceDataMerge();
		// 2.adapter 상태 전송 status가 변경된것만 전송
		sendAdapterStatus();

		if (logger.isInfoEnabled()) {
			logger.info("Adapters Status Send end");
		}
	}

	public HashMap<String, HashMap<String, Boolean>> getMemoryList() {
		ArrayList<AdaptersStatusVO> al = new ArrayList<AdaptersStatusVO>();
		// 인스턴스 별로 되어 있는걸 al 에 모음
		for (Map<String, String> map : ipList) {
			String inst = map.get("EAISVRINSTNM");
			String host = map.get("HOSTNAME");
			AdaptersStatusVO mVo = (AdaptersStatusVO) repository.getMonitorData(host, inst,
					AdaptersStatusVO.class.getName());
			if (mVo == null)
				continue;
			al.add(mVo);
		}
		HashMap<String, HashMap<String, Boolean>> hm = new HashMap<String, HashMap<String, Boolean>>();
		for (int i = 0; i < adapterList.size(); i++) {
			String key = adapterList.get(i);
			HashMap<String, Boolean> instance = new HashMap<String, Boolean>();
			for (int j = 0; j < al.size(); j++) {
				AdaptersStatusVO instVo = al.get(j);
				if (instVo == null)
					continue;
				boolean status = instVo.getAdapter(key);
				instance.put(instVo.getInstanceName(), status);

			}
			hm.put(key, instance);
		}
		return hm;
	}

	private void getInstanceDataMerge() {

		adapterStatusRepository.cleanupRepository();
		ArrayList<AdaptersStatusVO> al = new ArrayList<AdaptersStatusVO>();
		// HashMap<String,String> name = new HashMap<String,String>();
		// 인스턴스 별로 되어 있는걸 al 에 모음
		for (Map<String, String> map : ipList) {
			String inst = map.get("EAISVRINSTNM");
			String host = map.get("HOSTNAME");
			AdaptersStatusVO mVo = (AdaptersStatusVO) repository.getMonitorData(host, inst,
					AdaptersStatusVO.class.getName());
			if (mVo == null)
				continue;
			al.add(mVo);
		}
		// DB에서 사용함으로 되어 있는 어댑터 정보를 기준으로 메모리에 있는 접속상태 정보 가지고 옴
		for (int i = 0; i < adapterList.size(); i++) {
			String key = adapterList.get(i);
			boolean allStatus = false;
			for (int j = 0; j < al.size(); j++) {
				AdaptersStatusVO instVo = al.get(j);
				boolean status = false;
				status = instVo.getAdapter(key);
				if (status) {
					allStatus = true;
					break;
				}
			}
			AdapterStatusVO vo = (AdapterStatusVO) adapterStatusRepository.getAdapterStatus(key);
			if (vo == null) {
				vo = new AdapterStatusVO(key, key, allStatus, 0);
			} else {
				vo.setStatus(allStatus);
			}
			adapterStatusRepository.addAdapterStatus(key, vo);
		}
	}

	private void sendAdapterStatus() {
		ArrayList<String> list = adapterStatusRepository.getAdapterStatusNames();
		for (String adapterGroupName : list) {
			AdapterStatusVO sendVo = (AdapterStatusVO) adapterStatusRepository.getSendAdapterStatus(adapterGroupName);
			AdapterStatusVO vo = (AdapterStatusVO) adapterStatusRepository.getAdapterStatus(adapterGroupName);
			if (sendVo == null && vo == null) {
				// 전송안함
				continue;
			} else if (sendVo != null && vo == null) {
				// 사용안함으로 바뀐거
				sendVo = new AdapterStatusVO(sendVo.getAdapterGroupName(), sendVo.getCustomAdapterGroupName(), false,
						0);
				// 어댑터가 제거 된거기 때문에 상태정보가 무의미함..
				adapterStatusRepository.removeSendAdapterStatus(adapterGroupName);
			} else if (sendVo == null && vo != null) {
				// 전송함 (신규 추가된 )
				sendVo = new AdapterStatusVO(vo.getAdapterGroupName(), vo.getCustomAdapterGroupName(), vo.isStatus(),
						0);
				adapterStatusRepository.addSendAdapterStatus(adapterGroupName, sendVo);
			} else if (sendVo != null && vo != null) {
				// 전송함 정상적인상태
				if (sendVo.isStatus() == vo.isStatus()) {
					continue;
				} else {
					sendVo = new AdapterStatusVO(vo.getAdapterGroupName(), vo.getCustomAdapterGroupName(),
							vo.isStatus(), 0);
					adapterStatusRepository.addSendAdapterStatus(adapterGroupName, sendVo);
				}
			}

			executorServicePool.getExecutor().execute(new Sender(sendVo));

		}
	}

	private void send(AdapterStatusVO vo) throws Exception {

		String groupName = makeMessage(vo);
		if ("".equals(groupName))
			return;

		SendMessage sm = new SendMessage(groupName, vo.isStatus());

		// DB정보 갱신
//    	int result = 0;
//    	try{
//    		result = imaService.updateAdapterStatus(sm.getOtsdChnlDvcd(), sm.getOtsdChnlInstCd(), sm.getOsdchCoId(), sm.getSndrcvLnkDvcd(), sm.getLnkStcd());
//    	}catch (Exception e){
//    		
//    	}
//    	if (result == 0 ){
//    		throw new Exception("DB Update 실패");
//    	}

		// Data 전송
		String rmsAdpaterStatusSendYn = monitoringContext
				.getStringProperty(MonitoringContext.RMS_ADAPTER_STATUS_SEND_YN, "N");
		String url = monitoringContext.getStringProperty(MonitoringContext.RMS_ADAPTER_STATUS_SEND_URL, "");// http://192.168.120.139:30511/ESBWeb/EAIHttpRmsSyncAdapter

		if ("N".equals(rmsAdpaterStatusSendYn))
			throw new Exception("전송 안함");
		if ("".equals(url))
			throw new Exception("URL 셋팅 안되어 있음");
		;

//    	HttpClientAdapter hc = new HttpClientAdapter();
//    	
//    	
//    	
//    	Properties p = new Properties();
//    	p.put(HttpClientAdapter.URL, url);
//    	p.put(HttpClientAdapter.METHOD, HttpClientAdapter.METHOD_POST);
//    	p.put(HttpClientAdapter.ENCODE, "MS949");
//    	
//    	p.put(HttpClientAdapter.PARAMETER_NAME, "_message_");
		HttpClientAdapterMain hc = new HttpClientAdapterMain();
		Properties p = new Properties();
		p.put(HttpClientAdapterServiceKey.URL, url);
		p.put(HttpClientAdapterServiceKey.METHOD, HttpClientAdapterServiceKey.METHOD_POST);
		p.put(HttpClientAdapterServiceKey.ENCODE, "MS949");

		Object obj2 = hc.execute(p, sm.toMessage(), new Properties());
		String data = new String((byte[]) obj2);
		if (!"NR".equals(data.substring(28))) {
			throw new Exception("결과 오류");
		}
	}

	private String makeMessage(AdapterStatusVO vo) {

		String groupName = vo.getAdapterGroupName();
		if (groupName == null)
			return "";
		// 전송제거 목록 확인 %로 like검색 쓰임
		for (String data : block) {
			int index = data.indexOf("%");
			if (index == -1) {
				if (groupName.equals(data))
					return "";
			} else if (index == 0) {
				if (groupName.endsWith(data.substring(1)))
					return "";
			} else if (index + 1 == data.length()) {
				if (groupName.startsWith(data.substring(0, index)))
					return "";
			} else {
				if (groupName.startsWith(data.substring(0, index)) && groupName.endsWith(data.substring(index + 1)))
					return "";
			}
		}
		// 명칭 replace
		// 당발용과 타발용이 따로 있을경우 2개다 IO로 표시 되기때문에 당발용은 OU으로 타발용은 IN으로 표시하기위해서

		String rName = (String) replace.get(groupName);
		if (rName != null) {
			groupName = rName;
		}
		return groupName;
//    	return (new SendMessage(groupName,vo.isStatus())).toMessage();
	}

	class SendMessage {
		private String otsdChnlDvcd = null; // 대외채널구분코드 (3)
		private String otsdChnlInstCd = null; // 대외채널기관코드 (3)
		private String osdchCoId = null; // 대외채널업체ID (20)
		private String sndrcvLnkDvcd = null; // 송수신회선구분코드 (1) (1: 송신,2:수신,3:송수신)
		private String lnkStcd = null; // 회선상태코드 (1) (1: 정상,9:오류)
		private String procsRsltDvcd = null; // 처리결과구분코드 (2) (NR : 정상 , ER : 오류)

		SendMessage(String groupName, Boolean isStatus) {
			if (groupName.length() < 15)
				return;
			this.otsdChnlDvcd = groupName.substring(1, 4);
			if ("OU".equals(groupName.substring(5, 7))) {
				this.sndrcvLnkDvcd = "1";
			} else if ("IN".equals(groupName.substring(5, 7))) {
				this.sndrcvLnkDvcd = "2";
			} else if ("IO".equals(groupName.substring(5, 7))) {
				this.sndrcvLnkDvcd = "3";
			}
			if (isStatus) {
				this.lnkStcd = "1";
			} else {
				this.lnkStcd = "9";
			}

			if (groupName.indexOf("{") < 0)
				return;
			if (groupName.length() < 19)
				return;
			this.otsdChnlInstCd = groupName.substring(16, 19);

			if (groupName.substring(groupName.indexOf("{")).indexOf("_") < 0)
				return;
			this.osdchCoId = groupName.substring(groupName.lastIndexOf("_") + 1, groupName.indexOf("}"));
		}

		public String toMessage() {
			ByteBuffer bb = ByteBuffer.allocate(30);
			while (bb.hasRemaining()) {
				bb.put(" ".getBytes());
			}
			bb.position(0);
			if (otsdChnlDvcd != null) {
				bb.put(otsdChnlDvcd.getBytes());
			} else {
				bb.position(bb.position() + 3);
			}

			if (otsdChnlInstCd != null) {
				bb.put(otsdChnlInstCd.getBytes());
			} else {
				bb.position(bb.position() + 3);
			}

			if (osdchCoId != null) {
				bb.put(CommonUtil.rightPadString(osdchCoId, 20, " ").getBytes());
			} else {
				bb.position(bb.position() + 20);
			}

			if (sndrcvLnkDvcd != null) {
				bb.put(sndrcvLnkDvcd.getBytes());
			} else {
				bb.position(bb.position() + 1);
			}

			if (lnkStcd != null) {
				bb.put(lnkStcd.getBytes());
			} else {
				bb.position(bb.position() + 1);
			}
			if (procsRsltDvcd != null) {
				bb.put(procsRsltDvcd.getBytes());
			} else {
				bb.position(bb.position() + 2);
			}
			bb.position(0);
			byte[] data = new byte[30];
			bb.get(data);
			return new String(data);
		}

		public String getOtsdChnlDvcd() {
			return otsdChnlDvcd;
		}

		public void setOtsdChnlDvcd(String otsdChnlDvcd) {
			this.otsdChnlDvcd = otsdChnlDvcd;
		}

		public String getOtsdChnlInstCd() {
			return otsdChnlInstCd;
		}

		public void setOtsdChnlInstCd(String otsdChnlInstCd) {
			this.otsdChnlInstCd = otsdChnlInstCd;
		}

		public String getOsdchCoId() {
			return osdchCoId;
		}

		public void setOsdchCoId(String osdchCoId) {
			this.osdchCoId = osdchCoId;
		}

		public String getSndrcvLnkDvcd() {
			return sndrcvLnkDvcd;
		}

		public void setSndrcvLnkDvcd(String sndrcvLnkDvcd) {
			this.sndrcvLnkDvcd = sndrcvLnkDvcd;
		}

		public String getLnkStcd() {
			return lnkStcd;
		}

		public void setLnkStcd(String lnkStcd) {
			this.lnkStcd = lnkStcd;
		}

		public String getProcsRsltDvcd() {
			return procsRsltDvcd;
		}

		public void setProcsRsltDvcd(String procsRsltDvcd) {
			this.procsRsltDvcd = procsRsltDvcd;
		}
	}

	class Sender implements Runnable {
		private AdapterStatusVO sendVo;

		public Sender(AdapterStatusVO sendVo) {
			this.sendVo = sendVo;
		}

		public void run() {
			if (Thread.currentThread().getName().startsWith("pool-")) {
				Thread.currentThread().setName("AdapterStatusSender" + "-" + Thread.currentThread().getName());
			}
			try {
				send(sendVo);
			} catch (Exception e) {
				adapterStatusRepository.removeSendAdapterStatus(sendVo.getAdapterGroupName());
			}
		}

	}

}
