package com.eactive.eai.rms.onl.manage.serviceinfo.eaimsg;

import java.io.File;
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.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.eactive.eai.common.util.StringUtil;
import com.eactive.eai.common.util.SystemUtil;
import com.eactive.eai.data.entity.onl.message.EAIMessageEntity;
import com.eactive.eai.data.entity.onl.message.EAIReferenceMessage;
import com.eactive.eai.data.entity.onl.message.QEAIMessageEntity;
import com.eactive.eai.data.entity.onl.message.QServiceMessageEntity;
import com.eactive.eai.data.entity.onl.message.ServiceMessageEntity;
import com.eactive.eai.data.entity.onl.message.ServiceMessageEntityId;
import com.eactive.eai.data.entity.onl.routing.QRouting;
import com.eactive.eai.rms.common.base.OnlBaseService;
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.data.entity.onl.code.CodeService;
import com.eactive.eai.rms.data.entity.onl.eaimsg.EAIMessageService;
import com.eactive.eai.rms.data.entity.onl.eaimsg.EAIMessageUISearch;
import com.eactive.eai.rms.data.entity.onl.eaimsg.ServiceMessageService;
import com.eactive.eai.rms.data.entity.onl.layout.LayoutService;
import com.eactive.eai.rms.onl.common.exception.BizException;
import com.eactive.eai.rms.onl.common.exception.EMSRuntimeException;
import com.eactive.eai.rms.onl.common.util.FileWriteUtil;
import com.eactive.eai.rms.onl.loader.service.TransactionLoaderService;
import com.eactive.eai.rms.onl.manage.rule.upload.IoMapLoaderKey;
import com.eactive.eai.rms.onl.manage.serviceinfo.eaimsg.mapping.EAIMessageUIMapper;
import com.eactive.eai.rms.onl.manage.serviceinfo.eaimsg.mapping.EAIReferenceMessageUIMapper;
import com.eactive.eai.rms.onl.manage.serviceinfo.eaimsg.mapping.ServiceMessageUIMapper;
import com.eactive.eai.rms.onl.manage.serviceinfo.eaimsg.ui.EAIMessageUI;
import com.eactive.eai.rms.onl.manage.serviceinfo.eaimsg.ui.EAIReferenceMessageUI;
import com.eactive.eai.rms.onl.manage.serviceinfo.eaimsg.ui.ServiceMessageUI;
import com.querydsl.core.Tuple;

@Service
@Transactional
public class EaiMsgManService extends OnlBaseService {

	private MonitoringContext monitoringContext;
	
	private TransactionLoaderService loaderService;
	
	private EaiMsgCmFtpService eaiMsgCmFtpService;
	
	private EAIMessageService eaiMessageService;
	
	private EAIMessageUIMapper eaiMessageUIMapper;
	
	private ServiceMessageUIMapper serviceMessageUIMapper;
	
	private ServiceMessageService serviceMessageService;
	
	private EAIReferenceMessageUIMapper eaiReferenceMessageUIMapper;
	
	private LayoutService layoutService;
	
	private CodeService codeService;
	
	@Autowired
	public EaiMsgManService(MonitoringContext monitoringContext, TransactionLoaderService loaderService, EaiMsgCmFtpService eaiMsgCmFtpService, 
			EAIMessageService eaiMessageService, EAIMessageUIMapper eaiMessageUIMapper, ServiceMessageUIMapper serviceMessageUIMapper, 
			ServiceMessageService serviceMessageService,  EAIReferenceMessageUIMapper eaiReferenceMessageUIMapper,
			LayoutService layoutService, CodeService codeService){
		
		this.monitoringContext = monitoringContext;
		this.loaderService = loaderService;
		this.eaiMsgCmFtpService = eaiMsgCmFtpService;
		this.eaiMessageService = eaiMessageService;
		this.eaiMessageUIMapper = eaiMessageUIMapper;
		this.serviceMessageUIMapper = serviceMessageUIMapper;
		this.serviceMessageService = serviceMessageService;
		this.eaiReferenceMessageUIMapper = eaiReferenceMessageUIMapper;
		this.layoutService = layoutService;
		this.codeService = codeService;
	}
	
	
	@Transactional(readOnly = false, transactionManager = "transactionManager")
	public Page<EAIMessageUI> selectList(EAIMessageUISearch eaiMessageUISearch, Pageable pageable, String sortname,  String sortorder) {
		Page<Tuple> tuples = eaiMessageService.selectList(eaiMessageUISearch, pageable, sortname, sortorder);

		return tuples.map(tuple -> {
			EAIMessageEntity eaiMessageEntity = tuple.get(QEAIMessageEntity.eAIMessageEntity);
			String nonMotivRoutUriName = tuple.get(QRouting.routing.nonmotivrouturiname);
			String psvIntfaCdSticName = tuple.get(QServiceMessageEntity.serviceMessageEntity.psvintfacdsticname);

			EAIMessageUI eaiMessageUI = eaiMessageUIMapper.toVoWithoutChildren(eaiMessageEntity);
			eaiMessageUI.setNonMotivRoutUriName(nonMotivRoutUriName);
			eaiMessageUI.setPsvIntfaCdSticName(psvIntfaCdSticName);
			return eaiMessageUI;
		});
	}

	public Map<String, Object>[] selectListToExcel(EAIMessageUISearch eaiMessageUISearch) {

		List<Tuple> tuples = eaiMessageService.selectListToExcel(eaiMessageUISearch);

		@SuppressWarnings("unchecked")
		Map<String, Object>[] mapArray = tuples.stream().map(tuple -> {
			Map<String, Object> map = new HashMap<>();

			EAIMessageEntity eaiMessageEntity = tuple.get(QEAIMessageEntity.eAIMessageEntity);
			String nonMotivRoutUriName = tuple.get(QRouting.routing.nonmotivrouturiname);
			String psvIntfaCdSticName = tuple.get(QServiceMessageEntity.serviceMessageEntity.psvintfacdsticname);

			map.put("EAIBZWKDSTCD", eaiMessageEntity.getEaibzwkdstcd());
			map.put("EAISVCNAME", eaiMessageEntity.getEaisvcname());
			map.put("EAISVCDESC", eaiMessageEntity.getEaisvcdesc());
			map.put("SVCMOTIVUSEDSTCD", eaiMessageEntity.getSvcmotivusedstcd());
			map.put("PSVINTFACDSTICNAME", psvIntfaCdSticName);
			map.put("NONMOTIVROUTURINAME", nonMotivRoutUriName);
			map.put("FLOWCTRLROUTNAME", eaiMessageEntity.getFlowctrlroutname());

			String eaisevrdstcd = codeService
					.findCodeName("INOUT_TYPE", eaiMessageEntity.getEaisevrdstcd())
					.orElse("");
			String trantype = codeService.findCodeName("TRAN_TYPE", eaiMessageEntity.getTrantype()).orElse("");
			String useyn = codeService.findCodeName("USE_YN", eaiMessageEntity.getUseyn()).orElse("");
			String verinfo = codeService.findCodeName("VER_INFO", eaiMessageEntity.getVerinfo()).orElse("");

			map.put("EAISEVRDSTCD", eaisevrdstcd);
			map.put("TRANTYPE", trantype);
			map.put("USEYN", useyn);
			map.put("VERINFO", verinfo);

			return map;
		}).toArray(HashMap[]::new);

		return mapArray;
	}

	public EAIMessageUI selectDetail(String eaiSvcName) {
		return eaiMessageService.findById(eaiSvcName).map(eaiMessageUIMapper::toVo).orElse(new EAIMessageUI());
	}

	public ServiceMessageUI selectDetailSvc(String eaiSvcName, Integer svcPrcssNo) {

		ServiceMessageEntity serviceMessageEntity = serviceMessageService
				.getById(new ServiceMessageEntityId(eaiSvcName, svcPrcssNo));

		return serviceMessageUIMapper.toVo(serviceMessageEntity);
	}

	public void update(EAIMessageUI eaiMessageUI) {

		EAIMessageEntity eaiMessageEntity = eaiMessageService.getById(eaiMessageUI.getEaiSvcName());
		eaiMessageUIMapper.updateEntity(eaiMessageUI, eaiMessageEntity);
		eaiMessageService.save(eaiMessageEntity);
	}

	public List<EAIReferenceMessageUI> selectListRef(String eaiSvcName, Integer svcPrcssNo) {

		List<EAIReferenceMessage> refList = serviceMessageService
				.findById(new ServiceMessageEntityId(eaiSvcName, svcPrcssNo))
				.map(s -> s.getEaiReferenceMessages())
				.orElse(new ArrayList<>());

		List<EAIReferenceMessageUI> eaiReferenceMessageUIs = refList
				.stream()
				.map(eaiReferenceMessageUIMapper::toVo)
				.collect(Collectors.toList());

		eaiReferenceMessageUIs
				.forEach(e -> layoutService
						.findById(e.getRefMsgIdName())
						.ifPresent(l -> e.setLoutDesc(l.getLoutname())));

		return eaiReferenceMessageUIs;
	}

	public void updateTranType(String eaiSvcName, String tranType) {
		eaiMessageService.getById(eaiSvcName).setTrantype(tranType);
	}

	public void insert(EAIMessageUI eaiMessageUI) {
		EAIMessageEntity eaiMessageEntity = eaiMessageUIMapper.toEntity(eaiMessageUI);
		eaiMessageService.save(eaiMessageEntity);
	}

	public void delete(String eaiSvcName) {
		eaiMessageService.deleteById(eaiSvcName);
	}

	public void deleteSvc(ServiceMessageUI serviceMessageUI) {
		ServiceMessageEntity serviceMessageEntity = serviceMessageService.getById(
				new ServiceMessageEntityId(serviceMessageUI.getEaiSvcName(), serviceMessageUI.getSvcPrcssNo()));
		serviceMessageService.delete(serviceMessageEntity);
	}
	public void updateSvc(ServiceMessageUI serviceMessageUI) {
		ServiceMessageEntity serviceMessageEntity = serviceMessageService
				.getById(
						new ServiceMessageEntityId(serviceMessageUI.getEaiSvcName(), serviceMessageUI.getSvcPrcssNo()));

		serviceMessageUIMapper.updateEntity(serviceMessageUI, serviceMessageEntity);

		serviceMessageService.save(serviceMessageEntity);
	}

	public void insertSvc(ServiceMessageUI serviceMessageUI) {
		ServiceMessageEntity serviceMessageEntity = serviceMessageUIMapper.toEntity(serviceMessageUI);

		EAIMessageEntity eaiMessageEntity = eaiMessageService
				.getById(serviceMessageEntity.getId().getEaisvcname());

		int svcprcssno = serviceMessageEntity.getId().getSvcprcssno();
		serviceMessageEntity.getCondPerRtgInfos().forEach(c -> c.getId().setSvcprcssno(svcprcssno));
		serviceMessageEntity.getEaiReferenceMessages().forEach(e -> e.getId().setSvcprcssno(svcprcssno));
		
		serviceMessageEntity.setEaimessage(eaiMessageEntity);
		eaiMessageEntity.getServiceMessages().add(serviceMessageEntity);

		eaiMessageService.save(eaiMessageEntity);
	}

	public List<ServiceMessageUI> selectListSvc(String eaiSvcName) {
		return eaiMessageService
				.getById(eaiSvcName)
				.getServiceMessages()
				.stream()
				.map(serviceMessageUIMapper::toVo)
				.collect(Collectors.toList());
	}

	public boolean extractInterface(String userId, String listSvcName, String cmDate, String cmTime, String chgSvcName,
			String chgFlowControl) throws Exception {
		boolean ret = false;

		if (SystemUtil.isProdServer()) {
			throw new EMSRuntimeException("해당 시스템에선 사용할 수 없습니다.");
		}

		Properties prop = monitoringContext.getProperties();

		if (prop == null || prop.size() == 0) {
			throw new EMSRuntimeException("프라퍼티 정보가 없습니다. - CM_CONFIG");
		}

		boolean isDebug = logger.isDebugEnabled();
		String localPath = prop.getProperty(MonitoringContext.CM_AFTER_PATH, "/dat/eaiData/engineData/after/");
		String[] eaiSvcNames = listSvcName.split("\\,");

		String eaiSevrDstcd = null;
		HashMap<String, String> eaiSystemCode = new HashMap<>();
		for (String eaiSvcName : eaiSvcNames) {
			if (isDebug) {
				logger.debug("Start EAI 서비스  extract (eaiSvcName=" + eaiSvcName + ")");
			}

			EAIMessageEntity eaiMessageEntity = eaiMessageService
					.findById(eaiSvcName)
					.orElseThrow(() -> new BizException("서비스코드(" + eaiSvcName + ")가 없습니다"));

			// EAI서버구분코드 체크 없으면 Exception - EAISEVRDSTCD
			eaiSevrDstcd = StringUtil.getNullStr(eaiMessageEntity.getEaisevrdstcd());
			if (StringUtils.isBlank(eaiSevrDstcd)) {
				throw new BizException("EAI서버구분코드가 등록되지 않은 자원이 있습니다.");
			}
			// EAI서버구분코드가 다양하면 Exception

			eaiSystemCode.computeIfAbsent(eaiSevrDstcd, key -> key);

			if (eaiSystemCode.size() > 1) {
				throw new BizException("CM대상 자원의 EAI서버구분코드가 통일되지 않았습니다.");
			}

		}
		String eaiSevrName = IoMapLoaderKey.getEaiSevrName(eaiSevrDstcd);

		DataSourceType dt = DataSourceContextHolder.getDataSourceType();

		String folderName = cmDate + cmTime + "_" + userId + "_" + eaiSevrName + "_TXT";
		String path = localPath + folderName + File.separator + FileWriteUtil.INTERFACE + File.separator;

		try {
			for (String eaiSvcName : eaiSvcNames) {
				loaderService.interfaceDownLoading(dt, path, eaiSvcName);
			}
			eaiMsgCmFtpService.ftpUploadForStage(folderName, FileWriteUtil.INTERFACE, eaiSvcNames);
			ret = true;
		} catch (Exception e) {
			logger.error("ERROR: " + e.toString(), e);
			// 스테이징에 복사가 안되었을 경우 after에 생성된 폴더 삭제
			eaiMsgCmFtpService.deleteFolder(folderName);
			throw e;
		}
		return ret;
	}

	public void deleteAll(List<String> eaiSvcNames) {
		eaiSvcNames.forEach(eaiMessageService::deleteById);
	}
}
