package com.eactive.eai.rms.onl.transaction.online;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.eactive.eai.data.entity.onl.server.EAIServer;
import com.eactive.eai.data.entity.onl.unifbwk.UnifBwkTp;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.data.entity.onl.server.EAIServerService;
import com.eactive.eai.rms.data.entity.onl.transaction.TransactionTrackingFilter;
import com.eactive.eai.rms.data.entity.onl.transaction.TransactionTrackingFilterService;
import com.eactive.eai.rms.data.entity.onl.unifbwk.UnifBwkTpService;
import com.eactive.eai.rms.onl.adapter.socket.MapComparator;
import com.eactive.eai.rms.onl.common.service.EAISvcCodeMapService;
import com.eactive.eai.rms.onl.server.repository.TransactionRepositoryService;
import com.eactive.eai.rms.onl.vo.TransactionTrackingVo;

import edu.emory.mathcs.backport.java.util.Collections;

@Service("transactionOnlineService")
@Transactional
public class TransactionOnlineManService {

	private EAISvcCodeMapService eaiSvcCodeMapService;

	private TransactionRepositoryService transactionRepositoryService;

	private UnifBwkTpService unifBwkTpService;

	private EAIServerService eaiServerService;

	private TransactionTrackingFilterService transactionTrackingFilterService;

	private TransactionTrackingFilterUIMapper transactionTrackingFilterUIMapper;

	@Autowired
	public TransactionOnlineManService(TransactionRepositoryService transactionRepositoryService,
			UnifBwkTpService unifBwkTpService, EAIServerService eaiServerService,
			TransactionTrackingFilterService transactionTrackingFilterService,
			EAISvcCodeMapService eaiSvcCodeMapService,
			TransactionTrackingFilterUIMapper transactionTrackingFilterUIMapper) {
		this.unifBwkTpService = unifBwkTpService;
		this.eaiServerService = eaiServerService;
		this.transactionTrackingFilterService = transactionTrackingFilterService;
		this.eaiSvcCodeMapService = eaiSvcCodeMapService;
		this.transactionTrackingFilterUIMapper = transactionTrackingFilterUIMapper;
		this.transactionRepositoryService = transactionRepositoryService;
	}

	public List<TransactionTrackingVo> findTransactionList(List<EAIServer> instList) {

		TransactionTrackingVo origin = new TransactionTrackingVo();

		List<TransactionTrackingVo> list = new ArrayList<>();
		for (EAIServer eaiServer : instList) {

			origin.setHostName(eaiServer.getHostname());
			origin.setInstanceName(eaiServer.getEaisevrinstncname());

			TransactionTrackingVo vo = transactionRepositoryService.getTrackingVo(origin);

			if (vo != null) {
				vo.setService(DataSourceContextHolder.getDataSourceType().getName());
				list.add(vo);
			}
		}
		return list;
	}

	public List<TransactionOnlineUI> selectList(String userId, String recentYn) {
		// 권한이 등록리스트 TSEAIRM06
		List<UnifBwkTp> unifBwkTps = unifBwkTpService.findUserBizCodesByUserId(userId);
		List<EAIServer> instList = eaiServerService.selectEaiServerIpList();
		List<TransactionTrackingVo> transactionTrackings = findTransactionList(instList);

		// filter하고 싶은 업무코드 TSEAIMN04
		Set<String> filter = transactionTrackingFilterService
				.findAllByUserId(userId)
				.stream()
				.map(f -> f.getId().getEaibzwkdstcd())
				.collect(Collectors.toSet());

		List<TransactionOnlineUI> result = new ArrayList<>();
		DecimalFormat f1 = new DecimalFormat("##0.###");
		for (UnifBwkTp unifBwkTp : unifBwkTps) { // 권한이 부여된 리스트
			String bizCode = unifBwkTp.getEaibzwkdstcd();
			String bizName = unifBwkTp.getBzwkdsticname();

			if (!filter.contains(bizCode)) {
				continue;
			}
			selectList(recentYn, bizCode, bizName, transactionTrackings, result, f1);
		}
		return result;
	}

	private void selectList(String recentYn, String bizCode, String bizName,
			List<TransactionTrackingVo> transactionTrackings, List<TransactionOnlineUI> result, DecimalFormat f1) {
		Integer checkCount = 0;

		double timeEai = 0;
		double timePsv = 0;
		double timeTotal = 0;

		double countEai = 0;
		double countPsv = 0;
		double countTotal = 0;

		TransactionTrackingVo sum = new TransactionTrackingVo();
		for (TransactionTrackingVo transactionTrackingVo : transactionTrackings) {
			HashMap<String, HashMap<String, TransactionTrackingVo>> map;
			HashMap<String, TransactionTrackingVo> mapSub;

			if ("Y".equals(recentYn)) { // recent 10 mins
				map = transactionTrackingVo.getMapRecent();
			} else { // today
				map = transactionTrackingVo.getMapToday();
			}

			// EAI 서버에서 map 값이 null 인 상태로 값이 오는경우 Exception 발생을 방지하기 위함.
			if (map == null) {
				continue;
			}

			// DB 에 있는 업무코드는 CHAR[4] 타입이다.
			mapSub = map.get(bizCode);

			if (mapSub == null) {
				continue;
			}

			Iterator<String> it = mapSub.keySet().iterator();
			String[] svcKeys = new String[mapSub.size()]; // eai service code
			for (int j = 0; it.hasNext(); j++) {
				svcKeys[j] = it.next();
			}
			Arrays.sort(svcKeys);

			for (int k = 0; k < svcKeys.length; k++) {
				TransactionTrackingVo vo = mapSub.get(svcKeys[k]);

				sum.addProc100(vo.getProc100());
				sum.addProc200(vo.getProc200());
				sum.addProc300(vo.getProc300());
				sum.addProc400(vo.getProc400());
				sum.addError100(vo.getError100());
				sum.addError200(vo.getError200());
				sum.addError300(vo.getError300());
				sum.addError400(vo.getError400());
				sum.addError900(vo.getError900());
				sum.addError9300(vo.getError9300());
				sum.addError9400(vo.getError9400());
				sum.addError9900(vo.getError9900());

				// 공정한 평균값 을 계산하기 위해
				// 계산식을 수정한다.
				if (vo.getTimeEai() != 0 || vo.getTimePsv() != 0 || vo.getTimeTotal() != 0) {
					timeEai += vo.getTimeEai();
					countEai++;
					timePsv += vo.getTimePsv();
					countPsv++;
					timeTotal += vo.getTimeTotal();
					countTotal++;
				}

				checkCount++;

				sum.addTps(vo.getTps());
			} // for svcKeys
		}
		if (checkCount == 0)
			return; // There is no VO data !

		if (countEai == 0)
			countEai = 1;
		if (countPsv == 0)
			countPsv = 1;
		if (countTotal == 0)
			countTotal = 1;

		TransactionOnlineUI transactionOnlineUI = new TransactionOnlineUI();
		transactionOnlineUI.setBizCode(bizCode);
		transactionOnlineUI.setBizName(bizName);
		transactionOnlineUI.setProc100(sum.getProc100());
		transactionOnlineUI.setProc200(sum.getProc200());
		transactionOnlineUI.setProc300(sum.getProc300());
		transactionOnlineUI.setProc400(sum.getProc400());

		transactionOnlineUI.setError100(sum.getError100());
		transactionOnlineUI.setError200(sum.getError200());
		transactionOnlineUI.setError300(sum.getError300());
		transactionOnlineUI.setError400(sum.getError400());
		transactionOnlineUI.setError900(sum.getError900());

		transactionOnlineUI.setError9300(sum.getError9300());
		transactionOnlineUI.setError9400(sum.getError9400());
		transactionOnlineUI.setError9900(sum.getError9900());

		transactionOnlineUI.setAvgEai(f1.format(Double.isNaN(timeEai / countEai) ? 0 : (timeEai / countEai)));
		transactionOnlineUI.setAvgPsv(f1.format(Double.isNaN(timePsv / countPsv) ? 0 : (timePsv / countPsv)));
		transactionOnlineUI.setAvgTotal(f1.format(Double.isNaN(timeTotal / countTotal) ? 0 : (timeTotal / countTotal)));

		result.add(transactionOnlineUI);
	}

	public Map<String, Object> selectDetailList(String userId, String recentYn, String bwk, String searchFilter)
			throws Exception {
		// 권한이 등록리스트 TSEAIRM06
		List<UnifBwkTp> unifBwkTps = unifBwkTpService.findUserBizCodesByUserId(userId);
		List<EAIServer> instList = eaiServerService.selectEaiServerIpList();
		List<TransactionTrackingVo> transactionTrackings = findTransactionList(instList);

		// filter하고 싶은 업무코드 TSEAIMN04
		Set<String> filter = transactionTrackingFilterService
				.findAllByUserId(userId)
				.stream()
				.map(f -> f.getId().getEaibzwkdstcd())
				.collect(Collectors.toSet());

		HashMap<String, String> allEaiSvcMap = eaiSvcCodeMapService.selectAllMap();

		String[] arrFilter = null;
		if (!"".equals(searchFilter)) {
			arrFilter = searchFilter.split(",");
		}

		DecimalFormat f1 = new DecimalFormat("##0.###");

		List<HashMap<String, Object>> result = new ArrayList<>();
		Map<String, Object> resultMap = new HashMap<>();
		Map<String, Object> summaryMap = new HashMap<>();
		int p100 = 0;
		int p200 = 0;
		int p300 = 0;
		int p400 = 0;

		int e100 = 0;
		int e200 = 0;
		int e300 = 0;
		int e400 = 0;
		int e900 = 0;

		int e9300 = 0;
		int e9400 = 0;
		int e9900 = 0;

		double avgEai = 0.0f;
		double avgPsv = 0.0f;
		double avgTotal = 0.0f;

		int totCount = 0;

		for (UnifBwkTp unifBwkTp : unifBwkTps) { // 권한이 부여된 리스트
			String bizCode = unifBwkTp.getEaibzwkdstcd();
			String bizName = unifBwkTp.getBzwkdsticname();
			if (!filter.contains(bizCode))
				continue; // 보고싶은 업무구분코드 본다.
			if (!"".equals(bwk) && !bizCode.equals(bwk))
				continue; // select filter
			for (TransactionTrackingVo instVo : transactionTrackings) {
				HashMap<String, HashMap<String, TransactionTrackingVo>> map;
				HashMap<String, TransactionTrackingVo> mapSub;

				if ("Y".equals(recentYn)) { // recent 10 mins
					map = instVo.getMapRecent();
				} else { // today
					map = instVo.getMapToday();
				}

				// EAI 서버에서 map 값이 null 인 상태로 값이 오는경우 Exception 발생을 방지하기 위함.
				if (map == null) {
					continue;
				}

				// DB 에 있는 업무코드는 CHAR[4] 타입이다.
				mapSub = map.get(bizCode);

				if (mapSub == null) {
					continue;
				}

				Iterator<String> it = mapSub.keySet().iterator();
				String[] svcKeys = new String[mapSub.size()]; // eai service code
				for (int j = 0; it.hasNext(); j++) {
					svcKeys[j] = it.next();
				}
				Arrays.sort(svcKeys);

				for (int k = 0; k < svcKeys.length; k++) {
					TransactionTrackingVo vo = mapSub.get(svcKeys[k]);
					if (arrFilter != null) {
						boolean isFound = false;
						for (String f : arrFilter) {
							if (vo.getEaiServiceCode().indexOf(f) > -1) {
								isFound = true;
								break;
							}
						}

						if (!isFound) {
							continue;
						}
					}

					HashMap<String, Object> m = new HashMap<>();
					m.put("BIZCODE", bizCode);
					m.put("BIZNAME", bizName);

					m.put("SVCCODE", vo.getEaiServiceCode());
					m.put("SVCNAME", allEaiSvcMap.get(vo.getEaiServiceCode()));
					m.put("INSTNAME", instVo.getInstanceName());

					m.put("P100", vo.getProc100());
					m.put("P200", vo.getProc200());
					m.put("P300", vo.getProc300());
					m.put("P400", vo.getProc400());

					m.put("E100", vo.getError100());
					m.put("E200", vo.getError200());
					m.put("E300", vo.getError300());
					m.put("E400", vo.getError400());
					m.put("E900", vo.getError900());

					m.put("E9300", vo.getError9300());
					m.put("E9400", vo.getError9400());
					m.put("E9900", vo.getError9900());
					m.put("E9900", vo.getError9900());

					m.put("AVGEAI", vo.getTimeEai());
					m.put("AVGPSV", vo.getTimePsv());
					m.put("AVGTOTAL", vo.getTimeTotal());

					result.add(m);

					// 전문별 거래현황 합계
					p100 += vo.getProc100();
					p200 += vo.getProc200();
					p300 += vo.getProc300();
					p400 += vo.getProc400();

					e100 += vo.getError100();
					e200 += vo.getError200();
					e300 += vo.getError300();
					e400 += vo.getError400();
					e900 += vo.getError900();

					e9300 += vo.getError9300();
					e9400 += vo.getError9400();
					e9900 += vo.getError9900();

					avgEai += vo.getTimeEai();
					avgPsv += vo.getTimePsv();
					avgTotal += vo.getTimeTotal();

					if (vo.getProc400() > 0) {
						totCount += vo.getProc400();
					} else {
						totCount += vo.getProc200();
					}
				} // for svcKeys
			}

		}

		// 전문별 거래현황 합계
		summaryMap.put("P100", p100);
		summaryMap.put("P200", p200);
		summaryMap.put("P300", p300);
		summaryMap.put("P400", p400);

		summaryMap.put("E100", e100);
		summaryMap.put("E200", e200);
		summaryMap.put("E300", e300);
		summaryMap.put("E400", e400);
		summaryMap.put("E900", e900);

		summaryMap.put("E9300", e9300);
		summaryMap.put("E9400", e9400);
		summaryMap.put("E9900", e9900);

		if (totCount > 0) {
			summaryMap.put("AVGEAI", f1.format(avgEai / totCount));
			summaryMap.put("AVGPSV", f1.format(avgPsv / totCount));
			summaryMap.put("AVGTOTAL", f1.format(avgTotal / totCount));
		} else {
			summaryMap.put("AVGEAI", f1.format(0.0));
			summaryMap.put("AVGPSV", f1.format(0.0));
			summaryMap.put("AVGTOTAL", f1.format(0.0));
		}

		// 서비스별 합계 추가 start
		List<HashMap<String, Object>> sumList = new ArrayList<>();
		List<HashMap<String, Object>> onlySumList = new ArrayList<>();

		int sumP100 = 0;
		int sumP200 = 0;
		int sumP300 = 0;
		int sumP400 = 0;
		int sumE100 = 0;
		int sumE200 = 0;
		int sumE300 = 0;
		int sumE400 = 0;
		int sumE900 = 0;
		int sumE9300 = 0;
		int sumE9400 = 0;
		int sumE9900 = 0;

		double sumEAI = 0;
		double sumPSV = 0;
		double sumTOTAL = 0;

		String svcCode = "";
		String prevSvcCode = null;
		int divCount = 0;

		String prevBizname = "";
		String prevSvcname = "";

		String[] compareParam = { "BIZNAME", "SVCCODE", "INSTNAME" };

		MapComparator comp = new MapComparator(compareParam);
		Collections.sort(result, comp);

		for (int i = 0; i < result.size(); i++) {
			HashMap<String, Object> tmp = result.get(i);

			svcCode = (String) tmp.get("SVCCODE");

			if (prevSvcCode != null && !svcCode.equals(prevSvcCode)) {
				HashMap<String, Object> sumMap = new HashMap<>();

				divCount = sumP400;

				if (divCount == 0) {
					divCount = sumP200;
				}

				sumMap.put("P100", sumP100);
				sumMap.put("P200", sumP200);
				sumMap.put("P300", sumP300);
				sumMap.put("P400", sumP400);

				if (divCount > 0) {
					sumMap.put("AVGEAI", sumEAI / divCount);
					sumMap.put("AVGPSV", sumPSV / divCount);
					sumMap.put("AVGTOTAL", sumTOTAL / divCount);
					sumMap.put("AVGEAI", f1.format((double) sumMap.get("AVGEAI")));
					sumMap.put("AVGPSV", f1.format((double) sumMap.get("AVGPSV")));
					sumMap.put("AVGTOTAL", f1.format((double) sumMap.get("AVGTOTAL")));
				} else {
					sumMap.put("AVGEAI", f1.format(0));
					sumMap.put("AVGPSV", f1.format(0));
					sumMap.put("AVGTOTAL", f1.format(0));
				}

				sumMap.put("E100", sumE100);
				sumMap.put("E200", sumE200);
				sumMap.put("E300", sumE300);
				sumMap.put("E400", sumE400);
				sumMap.put("E900", sumE900);
				sumMap.put("E9300", sumE9300);
				sumMap.put("E9400", sumE9400);
				sumMap.put("E9900", sumE9900);
				sumMap.put("BIZNAME", (i == 0 ? tmp.get("BIZNAME") : prevBizname));
				sumMap.put("SVCNAME", (i == 0 ? tmp.get("SVCNAME") : prevSvcname));
				sumMap.put("INSTNAME", "합 계");
				sumMap.put("SVCCODE", prevSvcCode);

				sumList.add(sumMap);
				onlySumList.add(sumMap);
				sumP100 = 0;
				sumP200 = 0;
				sumP300 = 0;
				sumP400 = 0;

				sumEAI = 0;
				sumPSV = 0;
				sumTOTAL = 0;

				sumE100 = 0;
				sumE200 = 0;
				sumE300 = 0;
				sumE400 = 0;
				sumE900 = 0;

				sumE9300 = 0;
				sumE9400 = 0;
				sumE9900 = 0;
			}

			sumP100 += (Integer) tmp.get("P100");
			sumP200 += (Integer) tmp.get("P200");
			sumP300 += (Integer) tmp.get("P300");
			sumP400 += (Integer) tmp.get("P400");

			int totalCount = (Integer) tmp.get("P400");

			if (totalCount == 0) {
				totalCount = (Integer) tmp.get("P200");
			}
			sumEAI += ((double) tmp.get("AVGEAI") * totalCount);
			sumPSV += ((double) tmp.get("AVGPSV") * totalCount);
			sumTOTAL += ((double) tmp.get("AVGTOTAL") * totalCount);

			sumE100 += (Integer) tmp.get("E100");
			sumE200 += (Integer) tmp.get("E200");
			sumE300 += (Integer) tmp.get("E300");
			sumE400 += (Integer) tmp.get("E400");
			sumE900 += (Integer) tmp.get("E900");

			sumE9300 += (Integer) tmp.get("E9300");
			sumE9400 += (Integer) tmp.get("E9400");
			sumE9900 += (Integer) tmp.get("E9900");

			// Check
			tmp.put("AVGEAI", f1.format((double) tmp.get("AVGEAI")));
			tmp.put("AVGPSV", f1.format((double) tmp.get("AVGPSV")));
			tmp.put("AVGTOTAL", f1.format((double) tmp.get("AVGTOTAL")));

			sumList.add(tmp);
			prevSvcCode = svcCode;

			prevBizname = (String) tmp.get("BIZNAME");
			prevSvcname = (String) tmp.get("SVCNAME");
		}

		if (result != null && !result.isEmpty()) {
			HashMap<String, Object> sumMap = new HashMap<>();
			sumMap.put("P100", sumP100);
			sumMap.put("P200", sumP200);
			sumMap.put("P300", sumP300);
			sumMap.put("P400", sumP400);

			divCount = sumP400;

			if (divCount == 0) {
				divCount = sumP200;
			}

			if (divCount > 0) {
				sumMap.put("AVGEAI", sumEAI / divCount);
				sumMap.put("AVGPSV", sumPSV / divCount);
				sumMap.put("AVGTOTAL", sumTOTAL / divCount);

				// Check
				sumMap.put("AVGEAI", f1.format((double) sumMap.get("AVGEAI")));
				sumMap.put("AVGPSV", f1.format((double) sumMap.get("AVGPSV")));
				sumMap.put("AVGTOTAL", f1.format((double) sumMap.get("AVGTOTAL")));
			} else {
				sumMap.put("AVGEAI", f1.format(0));
				sumMap.put("AVGPSV", f1.format(0));
				sumMap.put("AVGTOTAL", f1.format(0));
			}

			sumMap.put("E100", sumE100);
			sumMap.put("E200", sumE200);
			sumMap.put("E300", sumE300);
			sumMap.put("E400", sumE400);
			sumMap.put("E900", sumE900);
			sumMap.put("E9300", sumE9300);
			sumMap.put("E9400", sumE9300);
			sumMap.put("E9900", sumE9300);
			sumMap.put("BIZNAME", prevBizname);
			sumMap.put("SVCNAME", prevSvcname);
			sumMap.put("INSTNAME", "합  계");
			sumMap.put("SVCCODE", prevSvcCode);

			sumList.add(sumMap);
			onlySumList.add(sumMap);
		}

		resultMap.put("sumList", sumList);
		resultMap.put("onlySumList", onlySumList);
		resultMap.put("list", result);
		resultMap.put("summary", summaryMap);

		return resultMap;
	}

	public List<TransactionTrackingFilterUI> selectListByUserOnlyUse(String userId) {
		List<TransactionTrackingFilterUI> transactionTrackingFilterUIs = transactionTrackingFilterService
				.findAllByUserId(userId)
				.stream()
				.map(transactionTrackingFilterUIMapper::toVo)
				.collect(Collectors.toList());

		transactionTrackingFilterUIs.forEach(ui -> {
			String bzwkdsticname = unifBwkTpService.getById(ui.getEaibzwkdstcd()).getBzwkdsticname();
			ui.setBzwkdsticname(bzwkdsticname);
		});

		return transactionTrackingFilterUIs;
	}

	public List<TransactionTrackingFilterUI> selectTransformTrackingFilterList(String userId) {

		List<TransactionTrackingFilterUI> transactionTrackingFilterUIs = unifBwkTpService
				.findUserBizCodesByUserId(userId)
				.stream()
				.map(transactionTrackingFilterUIMapper::toVo)
				.collect(Collectors.toList());

		Set<String> eaibzwkdstcds = transactionTrackingFilterService
				.findAllByUserId(userId)
				.stream()
				.map(t -> t.getId().getEaibzwkdstcd())
				.collect(Collectors.toSet());

		transactionTrackingFilterUIs.forEach(ui -> {
			if (eaibzwkdstcds.contains(ui.getEaibzwkdstcd())) {
				ui.setChk("true");
			} else {
				ui.setChk("false");
			}
		});

		return transactionTrackingFilterUIs;
	}

	public void insertTransactionTrackingFilter(String userId, List<TransactionTrackingFilterUI> uis) {

		List<TransactionTrackingFilter> oldFilters = transactionTrackingFilterService.findAllByUserId(userId);
		List<TransactionTrackingFilter> newFilters = uis
				.stream()
				.filter(ui -> "true".equals(ui.getChk()))
				.map(transactionTrackingFilterUIMapper::toEntity)
				.collect(Collectors.toList());
		newFilters.forEach(filter -> filter.getId().setUserid(userId));

		List<TransactionTrackingFilter> deleteFilters = oldFilters
				.stream()
				.filter(filter -> !newFilters.contains(filter))
				.collect(Collectors.toList());

		newFilters.removeIf(oldFilters::contains);
		transactionTrackingFilterService.saveAll(newFilters);
		transactionTrackingFilterService.deleteAll(deleteFilters);
	}

}
