package com.eactive.eai.rms.bat.monitoring.count;

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.eactive.eai.rms.bat.common.base.BatBaseService;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;

@Service("batCountMonitorService")
public class BatCountMonitorService extends BatBaseService {
	
	public enum CountMapType {
		FF, DD
	}
	
	private static final String KEY_SNDRCVKIND 			= "sndRcvKind";
	private static final String SNDRCVKIND_SEND 		= "1";
	private static final String SNDRCVKIND_RECV 		= "2";
	
	private static final String KEY_TRANSSTATUSCD 		= "transStatusCd";
	private static final String TRANSSTATUSCD_CLEAR 	= "C";
	private static final String TRANSSTATUSCD_STANDING 	= "S";
	private static final String TRANSSTATUSCD_FAIL 		= "F";
	
	@Autowired
	@Qualifier("batCountMonitorDao")
	private BatCountMonitorDao dao;
	
	public List<Map<String, Object>> selectList(HashMap<String, Object> paramap) throws Exception {
		return dao.selectList(paramap);
	}
	
	/**
	 * <b>Example</b>
	 * <br>Parameter Map 1 "sendClear": 100 "sendFail": 5 & Map2 "sendClear": 105 "sendFail": 10 
	 * <br>Return    Map r "sendClear": 205 "sendFail": 15 
	 * <pre>List내의 Map은 모두 동일한 Entry를 가진다고 가정함</pre>
	 */
	public Map<String, Integer> combineCountMaps(List<Map<String, Integer>> countMapList) {
		
		if(countMapList.size() == 0) {
			return new HashMap<>();
		}
		
		Map<String, Integer> resultCountMap = new HashMap<>();
		countMapList.get(0).entrySet().forEach(entry -> {
			resultCountMap.put(entry.getKey(), countMapList.stream().mapToInt(map -> map.get(entry.getKey())).sum());
		});
		
		return resultCountMap;
	}
	
	public Map<String, Integer> selectCountMap(Map<String, Object> paramMap, CountMapType type) throws Exception {
		
		String[] SNDRCVKIND_VALUES = { SNDRCVKIND_SEND, SNDRCVKIND_RECV };
		String[] TRANSSTATUSCD_VALUES = { TRANSSTATUSCD_CLEAR, TRANSSTATUSCD_STANDING, TRANSSTATUSCD_FAIL };
		int poolSize = SNDRCVKIND_VALUES.length*TRANSSTATUSCD_VALUES.length;
		
		ExecutorService poolExecutor = new ThreadPoolExecutor(poolSize, poolSize, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
		
		try {
			
			List<Future<Entry<String, Integer>>> futureBox = new ArrayList<>();
			for(String sndrcvkindValue : SNDRCVKIND_VALUES) {
				for(String transstatuscdValue : TRANSSTATUSCD_VALUES) {
					String entryPrefix = getEntryPrefix(sndrcvkindValue);
					String entrySuffix = getEntrySuffix(transstatuscdValue);
					Future<Entry<String, Integer>> taskResult = poolExecutor.submit(() -> {
						DataSourceContextHolder.setDataSourceType(DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.BAT));
						Map<String, Object> copiedMap = new HashMap<>(paramMap);
						copiedMap.put(KEY_SNDRCVKIND, 	sndrcvkindValue);
						copiedMap.put(KEY_TRANSSTATUSCD, transstatuscdValue);
						Integer countResult = dao.selectCount(copiedMap, type);
						return new SimpleEntry<String, Integer>(entryPrefix+entrySuffix, countResult);
					});
					futureBox.add(taskResult);
				}
			}
			
			Map<String, Integer> resultCountMap = new HashMap<>();
			for(Future<Entry<String, Integer>> future : futureBox) {
				Entry<String, Integer> entry = future.get(15, TimeUnit.SECONDS);
				resultCountMap.put(entry.getKey(), entry.getValue());
			}
			
			resultCountMap.put("sendAll", resultCountMap.entrySet().stream().filter(entry -> entry.getKey().startsWith("send")).mapToInt(e -> e.getValue()).sum());
			resultCountMap.put("recvAll", resultCountMap.entrySet().stream().filter(entry -> entry.getKey().startsWith("recv")).mapToInt(e -> e.getValue()).sum());
			
			return resultCountMap;
			
		} catch (Exception e) {
			logger.error("", e);
			throw e;
		} finally {
			poolExecutor.shutdown();
		}

	}

	private String getEntryPrefix(String sndrcvkindValue) {
		switch (sndrcvkindValue) {
		case SNDRCVKIND_SEND:
			return "send";
		case SNDRCVKIND_RECV:
			return "recv";
		default:
			return "ERR";
		}
	}
	
	private String getEntrySuffix(String transstatuscdValue) {
		switch (transstatuscdValue) {
		case TRANSSTATUSCD_CLEAR:
			return "Clear";
		case TRANSSTATUSCD_STANDING:
			return "Standing";
		case TRANSSTATUSCD_FAIL:
			return "Fail";
		default:
			return "ERR";
		}
	}

}
