package com.eactive.eai.rms.onl.manage.adapter.adapter;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.eactive.eai.agent.command.Command;
import com.eactive.eai.agent.command.CommonCommand;
import com.eactive.eai.common.stdmessage.STDMessageManager;
import com.eactive.eai.data.entity.onl.adapter.Adapter;
import com.eactive.eai.data.entity.onl.adapter.AdapterGroup;
import com.eactive.eai.data.entity.onl.adapter.AdapterProp;
import com.eactive.eai.data.entity.onl.adapter.AdapterPropGroup;
import com.eactive.eai.data.entity.onl.stdmessage.StandardMessageInfo;
import com.eactive.eai.data.jpa.BaseRepository;
import com.eactive.eai.rms.common.acl.monitoringCode.MonitoringCodeManService;
import com.eactive.eai.rms.common.base.OnlBaseService;
import com.eactive.eai.rms.common.comDomain.CommonDomainManService;
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.spring.LocaleMessage;
import com.eactive.eai.rms.common.util.CommonUtil;
import com.eactive.eai.rms.data.entity.onl.adapter.AdapterGroupService;
import com.eactive.eai.rms.data.entity.onl.adapter.AdapterPropService;
import com.eactive.eai.rms.data.entity.onl.adapter.AdapterTypeProperty;
import com.eactive.eai.rms.data.entity.onl.adapter.AdapterTypePropertyService;
import com.eactive.eai.rms.data.entity.onl.adapter.CommonDomain;
import com.eactive.eai.rms.data.entity.onl.code.CodeService;
import com.eactive.eai.rms.data.entity.onl.server.EAIServerService;
import com.eactive.eai.rms.data.entity.onl.stdmessage.StandardMessageInfoService;
import com.eactive.eai.rms.onl.common.exception.BizException;
import com.eactive.eai.rms.onl.common.service.AdapterChangeThreadPool;
import com.eactive.eai.rms.onl.manage.adapter.adapter.mapper.AdapterGroupUIMapper;
import com.eactive.eai.rms.onl.manage.adapter.adapter.mapper.AdapterUIMapper;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterExportData;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUI;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUIMixIn;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUISearch;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterPropUI;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterUI;
import com.ext.eai.agent.stdmessage.ReloadSTDMessageCommand;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
@Transactional
public class AdapterManService extends OnlBaseService {

	private static Object obj = new Object();

	@Autowired
	@Qualifier("adapterDao")
	private AdapterDao dao;

	@Autowired
	@Qualifier("monitoringCodeManService")
	private MonitoringCodeManService monitoringCodeService;

	@Autowired
	private CommonDomainManService domainService;

	@Autowired
	@Qualifier("monitoringContext")
	private MonitoringContext monitoringContext;

	@Autowired
	@Qualifier("localeMessage")
	private LocaleMessage localeMessage;

	@Autowired
	private AdapterGroupUIMapper adapterGroupUIMapper;

	@Autowired
	private AdapterTypePropertyService adapterTypePropertyService;

	@Autowired
	private CodeService codeService;

	@Autowired
	private AdapterUIMapper adapterUIMapper;

	@Autowired
	private AdapterPropService adapterPropLoader;

	@Autowired
	private AdapterGroupService adapterGroupService;
	
	@Autowired
	private EAIServerService eaiServerService;
	
	@Autowired
	private StandardMessageInfoService standardmessageinfoService;
	
	public Page<AdapterGroupUI> selectList(Pageable pageable, AdapterGroupUISearch adapterGroupUISearch) {
		Page<AdapterGroup> page = adapterGroupService.findPage(pageable, adapterGroupUISearch);
		return page.map(adapterGroupUIMapper::toVoWithoutChildrenMapping);
	}

	@SuppressWarnings("unchecked")
	public Map<String, Object>[] selectListToExcel(AdapterGroupUISearch adapterGroupUISearch) {
		Pageable pageableWithSort = PageRequest.of(0, Integer.MAX_VALUE, Sort.by(Sort.Direction.ASC, "adptrbzwkgroupname"));
		 
		List<AdapterGroupUI> adapterGroupUis = this.selectList(pageableWithSort, adapterGroupUISearch).getContent();

		return adapterGroupUis.stream().map(adapterGroupUI -> {
			Map<String, Object> map = new HashMap<>();
			map.put("ADPTRBZWKGROUPNAME", adapterGroupUI.getAdptrbzwkgroupname());
			map.put("ADPTRBZWKGROUPDESC", adapterGroupUI.getAdptrbzwkgroupdesc());
			map.put("ADPTRIODSTCD", adapterGroupUI.getAdptriodstcd());
			map.put("ADPTRMSGPTRNCD", adapterGroupUI.getAdptrmsgptrncd());
			map.put("EAIBZWKDSTCD", adapterGroupUI.getEaibzwkdstcd());
			map.put("ADPTRMSGDSTCD", adapterGroupUI.getAdptrmsgdstcd());
			map.put("ADPTRUSEYN", adapterGroupUI.getAdptruseyn());

			codeService
					.findCodeName("USE_YN", adapterGroupUI.getAdptruseyn())
					.ifPresent(codeName -> map.put("ADPTRUSEYN", codeName));
			codeService
					.findCodeName("ADPTR_MSG_PTRN_CD", adapterGroupUI.getAdptrmsgptrncd())
					.ifPresent(codeName -> map.put("ADPTRMSGPTRNCD", codeName));

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

	}

	public AdapterGroupUI selectDetail(String adptrBzwkGroupName) {
		AdapterGroup adapterGroup = adapterGroupService.getById(adptrBzwkGroupName);
		AdapterGroupUI adapterGroupUI = adapterGroupUIMapper.toVo(adapterGroup);

		/*
		 * 어댑터 타입에 해당 하는 property 정보(프로퍼티 설명, 도메인명 등)
		 */
		List<AdapterTypeProperty> adapterTypeProperties = (List<AdapterTypeProperty>) adapterTypePropertyService
				.findByAdapterGroup(adapterGroup);

		/*
		 * 화면에서 사용되는 property 표현 형식, SELECT, OPTION등에 대한 도메인 정보를 조회 해 온다.
		 */
		Map<String, Object> domains = fetchDomains(adapterTypeProperties);
		adapterGroupUI.setDInfo(domains);

		/*
		 * DB에는 설정한 property값만 저장 되어 있기 때문에 어댑터에서 사용 하는 모든 프라퍼티의 List 정보를 다시 구성 한다.
		 */
		setAdapterPropUIs(adapterGroupUI, adapterTypeProperties, domains, adptrBzwkGroupName);

		return adapterGroupUI;
	}

	private Map<String, Object> fetchDomains(Iterable<AdapterTypeProperty> adapterTypeProperties) {
		Map<String, Object> domains = new HashMap<>();
		// adapter type에 따른 domain정보 구성
		adapterTypeProperties.forEach(adapterTypeProperty -> {
			if (StringUtils.isNotBlank(adapterTypeProperty.getDmnKnd())) {
				domains
						.computeIfAbsent(adapterTypeProperty.getDmnKnd(),
								k -> domainService.selectDomainInfo(adapterTypeProperty.getDmnKnd()));
			}
		});
		return domains;
	}

	private void setAdapterPropUIs(AdapterGroupUI adapterGroupUI, List<AdapterTypeProperty> adapterTypeProperties,
			Map<String, Object> domains, String adptrBzwkGroupName) {
		for (AdapterUI adapterUI : adapterGroupUI.getAdapters()) {
			List<AdapterPropUI> adapterPropUIs = new ArrayList<>();

			for (AdapterTypeProperty adapterTypeProperty : adapterTypeProperties) {
				AdapterPropUI propUI = createAdapterPropUI(adptrBzwkGroupName, adapterTypeProperty, domains,
						adapterUI.getAdapterPropGroup().getAdapterProps());
				adapterPropUIs.add(propUI);
			}

			adapterUI.setAdapterProps(adapterPropUIs);
		}
	}

	@SuppressWarnings("unchecked")
	private AdapterPropUI createAdapterPropUI(String adptrBzwkGroupName, AdapterTypeProperty atp,
			Map<String, Object> domains, List<AdapterPropUI> adapterProps) {
		/*
		 * 저장된 property 값과 어댑터가 가져야 하는 property의 값을 합하여 grid에 출력할 AdapterPropUI 를 구성 한다.
		 */

		// 화면에서 그리드에 출력되는 프로퍼티
		AdapterPropUI adapterPropUI = new AdapterPropUI();
		adapterPropUI.setPrptygroupname(adptrBzwkGroupName);
		adapterPropUI.setPrptyname(atp.getId().getAdptrPrptyName());
		adapterPropUI.setAdptrPrptyDesc(atp.getAdptrPrptyDesc());
		adapterPropUI.setDmnKnd(atp.getDmnKnd());

		if (StringUtils.isNotBlank(atp.getDmnKnd())) {
			Map<String, Object> domainMap = (Map<String, Object>) domains.get(atp.getDmnKnd());
			CommonDomain commonDomain = (CommonDomain) domainMap.get("info");
			adapterPropUI.setDomainType(commonDomain.getDomainType());
		}

		Optional<AdapterPropUI> matchingPropOptional = adapterProps
				.stream()
				.filter(adapterProp -> adapterProp.getPrptyname().equals(adapterPropUI.getPrptyname()))
				.findFirst();
		// db에 저장 되어 있는 property 값을 설정
		matchingPropOptional.ifPresent(matchingProp -> adapterPropUI.setPrpty2val(matchingProp.getPrpty2val()));

		return adapterPropUI;
	}

	public List<AdapterUI> selectListAdapter(String adptrBzwkGroupName) {
		AdapterGroup adapterGroup = adapterGroupService.getById(adptrBzwkGroupName);
		return adapterGroup.getAdapters().stream().map(adapterUIMapper::toVo).collect(Collectors.toList());
	}

	public void insert(AdapterGroupUI adapterGroupUI) {
		AdapterGroup adapterGroup = adapterGroupUIMapper.toEntity(adapterGroupUI);
		adapterGroupService.save(adapterGroup);
	}

	public void update(AdapterGroupUI adapterGroupUI) {

	    AdapterGroup adapterGroup = adapterGroupService.getById(adapterGroupUI.getAdptrbzwkgroupname());
	    
	    Map<String, String> oldApiPathMap = extractApiPaths(adapterGroup);
	    
	    adapterGroupUIMapper.updateEntity(adapterGroupUI, adapterGroup);
	    
	    // NonUniqueObjectException 대응
	    adapterGroupService.saveAndFlush(adapterGroup);

	    
	    // HS04 - API_FULL_PATH 동기화
	    syncIfApiPathChanged(adapterGroup, oldApiPathMap);
	}
	
	private Map<String, String> extractApiPaths(AdapterGroup adapterGroup) {
		
		Map<String, String> pathMap = new HashMap<>();
		
		if (adapterGroup == null || adapterGroup.getAdapters() == null) {
	        return pathMap;
	    }
		for (Adapter adapter : adapterGroup.getAdapters()) {
			
			if (adapter.getAdapterPropGroup() == null 
	                || adapter.getAdapterPropGroup().getAdapterProps() == null) {
	            continue; 
	        }
			
			for (AdapterProp prop : adapter.getAdapterPropGroup().getAdapterProps()) {
				String propName = prop.getId().getPrptyname();
				
				if ("API_PATH".equals(propName)) {
					String adapterName = adapter.getId().getAdptrbzwkgroupname(); 
	                
	                String apiPathValue = prop.getPrpty2val(); 

	                pathMap.put(adapterName, apiPathValue);
	                
	                break;
	                
				}
				
			}
		}
		
		
		return pathMap;
		
	}
	
	private void syncIfApiPathChanged(AdapterGroup newGroup, Map<String, String> oldPathMap) {
		
		if (newGroup.getAdapters() == null) return;
		
		for (Adapter newAdapter : newGroup.getAdapters()) { // adapterData 분리
			String adapterName = newAdapter.getId().getAdptrbzwkgroupname(); // 업데이트할때 들어온 어댑터 이름
			String newPath = findApiPathValue(newAdapter);
			
			String oldPath = oldPathMap.get(adapterName);
			
			if (StringUtils.isNotBlank(oldPath) 
                    && StringUtils.isNotBlank(newPath) 
                    && !StringUtils.equals(oldPath, newPath)) {

                updateStdMessageInfoApiFullPath(adapterName, oldPath, newPath);
            }
		}
		
		
	}
	
	private String findApiPathValue(Adapter adapter) {
        if (adapter.getAdapterPropGroup() == null 
                || adapter.getAdapterPropGroup().getAdapterProps() == null) {
            return null;
        }

        return adapter.getAdapterPropGroup().getAdapterProps().stream()
        		.filter(prop -> prop.getId() != null && "API_PATH".equals(prop.getId().getPrptyname()))
                .findFirst()
                .map(AdapterProp::getPrpty2val) 
                .orElse(null);
    }
	
	private void updateStdMessageInfoApiFullPath(String adapterName, String oldPath, String newPath) {
		
		List<StandardMessageInfo> stdMsgInfoList = standardmessageinfoService.findByBzwksvckeynameStartingWith(adapterName);
		
		// 변경 사항이 있었는지 체크해서 HS04 Reload 함.
        for (StandardMessageInfo stdMsgInfo : stdMsgInfoList) {
             String fullPath = stdMsgInfo.getApifullpath(); 

            // 기존 경로(oldPath)를 포함하고 있을 때만 교체
            if (StringUtils.isNotBlank(fullPath) && fullPath.contains(oldPath)) {
            	
            	int pipeIndex = fullPath.indexOf("|");
            	
            	if (pipeIndex != -1) {
            	    String methodPart = fullPath.substring(0, pipeIndex + 1);
            	    String uriPart = fullPath.substring(pipeIndex + 1);

            	    if (uriPart.startsWith(oldPath)) {
            	        String newUriPart = newPath + uriPart.substring(oldPath.length());
            	        String updatedFullPath = methodPart + newUriPart;
            	        
            	        stdMsgInfo.setApifullpath(updatedFullPath);
            	        
            	        
            	        try {
            	        	// DB에 즉시 반영 
            	        	standardmessageinfoService.saveAndFlush(stdMsgInfo);
            	        	
						} catch (Exception e) {
							
							log.error("StandardMessageInfo APIFULLPATH SAVE FAIL key = {}", stdMsgInfo.getBzwksvckeyname());
						}
            	        
            	    }
            	} else {
            	    // 파이프가 없는 경우 로그 생성
            		log.warn("[StandardMessageInfo APIFULLPATH Sync Skip] Abnormal data format found. Adapter: {}, FullPath: {}", adapterName, fullPath);
            	}
                
            }
        }
        
    }

	public void delete(AdapterGroupUI adapterGroupUI) {
		adapterGroupService.deleteById(adapterGroupUI.getAdptrbzwkgroupname());
	}

	public void insertClone(AdapterGroupUI adapterGroupUI) {
		String newAdptrBzwkGroupName = adapterGroupUI.getNewadptrbzwkgroupname();
		String oldAdptrBzwkGroupName = adapterGroupUI.getAdptrbzwkgroupname();

		if (adapterGroupService.existsById(newAdptrBzwkGroupName)) {
			throw new BizException("중복된 어댑터그룹명이 존재합니다.");
		}

		AdapterGroup adapterGroup = adapterGroupUIMapper.toEntity(adapterGroupUI);

		adapterGroup.setAdptrbzwkgroupname(newAdptrBzwkGroupName);// 새로운 어댑터 명으로 변경
		adapterGroup.setAdptruseyn("0"); // 복제후 사용안함으로 생성

		if (!"Y".equals(adapterGroupUI.getCloneyn())) {
			// 신규 일 경우 1번 어댑터 정보만 복사
			adapterGroup.getAdapters().stream().findFirst().ifPresent(adapter -> {
				adapterGroup.getAdapters().clear();
				adapterGroup.getAdapters().add(adapter);
			});
		}

		// property 복제
		adapterGroup
				.getAdapters()
				.stream()
				.forEach(a -> clonePropGroup(a, newAdptrBzwkGroupName, oldAdptrBzwkGroupName));

		adapterGroupService.save(adapterGroup);
	}

	public void clonePropGroup(Adapter adapter, String newAdptrBzwkGroupName, String oldAdptrBzwkGroupName) {

		int imsiCnt = 1;

		String oldPrptyGroupName = adapter.getPrptygroupname();
		String newPrptyGroupNameStd = "";
		String appendString = "";

		String oldAdapterGroupNameTmp = "";
		String oldAdapterGroupNameStd = "";
		String oldPrptyGroupNameStd = "";

		// {문자열} -> 문자열 추출
		if (StringUtils.contains(oldPrptyGroupName, '{')) {
			oldPrptyGroupNameStd = oldPrptyGroupName
					.substring(oldPrptyGroupName.indexOf("{") + 1, oldPrptyGroupName.indexOf("}"));
		}

		// 추가
		if (StringUtils.contains(oldAdptrBzwkGroupName, '{')) {
			oldAdapterGroupNameStd = oldAdptrBzwkGroupName
					.substring(oldAdptrBzwkGroupName.indexOf("{") + 1, oldAdptrBzwkGroupName.indexOf("}"));
		}

		// {} 안에 어댑터명이 있을경우 제거
		if (oldAdptrBzwkGroupName.startsWith("_")) {
			oldAdapterGroupNameTmp = oldAdptrBzwkGroupName.replaceFirst("_", "");
		}

		if (oldPrptyGroupNameStd.indexOf(oldAdapterGroupNameTmp) != -1) {
			oldPrptyGroupNameStd = oldPrptyGroupNameStd.replace(oldAdapterGroupNameTmp, "");
		}

		if (!"".equals(oldAdapterGroupNameStd)) {
			appendString = oldPrptyGroupNameStd.replace(oldAdapterGroupNameStd, "");
		}

		if ("".equals(appendString)) {
			if (!"".equals(oldPrptyGroupNameStd)) {
				appendString = oldPrptyGroupNameStd;
			} else {
				appendString = "i1" + imsiCnt;
			}
		}

		if (newAdptrBzwkGroupName.indexOf("{") != -1)
			newPrptyGroupNameStd = newAdptrBzwkGroupName
					.substring(newAdptrBzwkGroupName.indexOf("{") + 1, newAdptrBzwkGroupName.indexOf("}")) + "_"; // GBN

		if (appendString.startsWith("_")) {
			appendString = appendString.replaceFirst("_", "");
		}

		newPrptyGroupNameStd = newPrptyGroupNameStd + appendString;

		String prptyGroupName = newAdptrBzwkGroupName.split("[{]")[0] + "{" + newPrptyGroupNameStd + "}";

		adapter.getId().setAdptrbzwkname(prptyGroupName);
		adapter.setPrptygroupname(prptyGroupName);
		adapter.getId().setAdptrbzwkgroupname(adapter.getAdapterGroup().getAdptrbzwkgroupname());

		if (adapter.getAdapterPropGroup() != null) {
			cloneAdapterPropGroup(adapter);
		}

	}

	private void cloneAdapterPropGroup(Adapter adapter) {
		AdapterPropGroup adapterPropGroup = adapter.getAdapterPropGroup();
		adapterPropGroup.setPrptygroupname(adapter.getPrptygroupname());
		// 값이 없으면 저장 하지 않음
		adapterPropGroup.getAdapterProps().removeIf(prop -> StringUtils.isBlank(prop.getPrpty2val()));

		adapterPropGroup.getAdapterProps().forEach(prop -> {
			prop.getId().setPrptygroupname(adapter.getPrptygroupname());
			prop.setAdapterPropGroup(adapterPropGroup);
			if ("adapter.group.name".equals(prop.getId().getPrptyname())) {
				prop.setPrpty2val(adapter.getId().getAdptrbzwkgroupname());
			}
		});
	}

	public Map<String, Object> selectListAdapterProp(String adptrCd, String adptrIoDstcd) {
		Iterable<AdapterTypeProperty> adapterTypeProperties = adapterTypePropertyService
				.findByIdAdptrCdAndAdptrIoDstcd(adptrCd, adptrIoDstcd);

		Map<String, Object> dInfo = fetchDomains(adapterTypeProperties);
		List<AdapterPropUI> adapterPropUIs = StreamSupport
				.stream(adapterTypeProperties.spliterator(), false)
				.map(atp -> createAdapterPropUI("", atp, dInfo, new ArrayList<>()))
				.collect(Collectors.toList());

		HashMap<String, Object> retMap = new HashMap<>();
		retMap.put("rows", adapterPropUIs);
		retMap.put("dInfo", dInfo);
		return retMap;
	}

	@SuppressWarnings("unchecked")
	public List<Object> selectListDomain(String domainName) {
		if (StringUtils.isBlank(domainName)) {
			return new ArrayList<>();
		}
		Map<String, Object> domain = domainService.selectDomainInfo(domainName);
		return (List<Object>) Optional.ofNullable(domain).map(d -> d.get("data")).orElse(null);
	}

	public List<AdapterGroupUI> selectListComboForAdptrIoDstcd(String adptrIoDstcd) {
		Iterable<AdapterGroup> adapterGroups = adapterGroupService.findByAdptrIoDstcd(adptrIoDstcd);
		return StreamSupport
				.stream(adapterGroups.spliterator(), false)
				.map(adapterGroupUIMapper::toVoWithoutChildrenMapping)
				.collect(Collectors.toList());
	}

	// 어댑터 대외이관 정보 update
	public void updateAdptOsidinstiInfo(AdapterGroupUI adapterGroupUI) {
		AdapterGroup adapterGroup = adapterGroupService.getById(adapterGroupUI.getAdptrbzwkgroupname());

		adapterGroup.setAdptrbzwkgroupdesc(adapterGroupUI.getAdptrbzwkgroupdesc());
		adapterGroup.setOsidinstiyn(adapterGroupUI.getOsidinstiyn());
		adapterGroup.setOsidinstibzwkrsempname(adapterGroupUI.getOsidinstibzwkrsempname());
		adapterGroup.setOsidinstitelno(adapterGroupUI.getOsidinstitelno());
		adapterGroup.setMonicndnctnt(adapterGroupUI.getMonicndnctnt());
		adapterGroup.setEaisevrdstcd(adapterGroupUI.getEaisevrdstcd());
		adapterGroup.setCirnumber(adapterGroupUI.getCirnumber());
		adapterGroup.setDradptryn(adapterGroupUI.getDradptryn());
		adapterGroup.setAdptrinsticode(adapterGroupUI.getAdptrinsticode());

		if (StringUtils.isNotBlank(adapterGroup.getMoniuseyn())) {
			adapterGroup.setMoniuseyn(adapterGroupUI.getMoniuseyn());
		}
	}
	
	public Map<String, String> reloadSyncStdMsgAndAdapter(AdapterGroupUI adapterGroupUI) throws Exception {
	    Map<String, Object> receive = null;
	    Map<String, String> result = new HashMap<>();
	    String status = "success";

	    try {
	        log.info(">>>>>> [Reload AdapterManService Start] 리로드 시작");

	        for (Entry<String, AdapterUI> entry : adapterGroupUI.getAdapterData().entrySet()) {
	            AdapterUI adapterUI = entry.getValue();
	            String prptyGroupName = adapterUI.getPrptygroupname();
	            
	            CommonCommand pcommand = new CommonCommand(
	                    "com.eactive.eai.agent.adapter.ReloadAdapterPropGroupCommand",
	                    prptyGroupName
	            );
	            agentUtilService.broadcast(pcommand);
	        }

	        // 어댑터 그룹 리로드
	        CommonCommand acommand = new CommonCommand(
	                "com.eactive.eai.agent.adapter.ReloadAdapterGroupCommand",
	                adapterGroupUI.getAdptrbzwkgroupname()
	        );
	        receive = agentUtilService.broadcast(acommand);

	        // HS04 API_FULL_PATH
	        Command stdcommand = new ReloadSTDMessageCommand();
	        stdcommand.setArgs("ALL");
	        agentUtilService.broadcast(stdcommand);
	        
	        log.info(">>>>>> [Reload AdapterManService End] 리로드 완료");

	    } catch (Exception e) {
	        status = "error";
	        log.error("리로드 실패", e);
	    }

	    if (receive != null && receive.values().stream().anyMatch(value -> value == null || !"success".equals(value))) {
	        status = "error";
	    }
	    
	    result.put("status", status);
	    if(receive != null) result.put("message", receive.toString());
	    
	    return result;
	}
	
	public Map<String, String> reloadSync(AdapterGroupUI adapterGroupUI) throws Exception {
		Map<String, Object> receive = null;
		Map<String, String> result = new HashMap<>();
		String status = "success";
		for (Entry<String, AdapterUI> entry : adapterGroupUI.getAdapterData().entrySet()) {
			AdapterUI adapterUI = entry.getValue();
			String prptyGroupName = adapterUI.getPrptygroupname();
			CommonCommand pcommand = new CommonCommand("com.eactive.eai.agent.adapter.ReloadAdapterPropGroupCommand",
					prptyGroupName);
			agentUtilService.broadcast(pcommand);
		}
		
		CommonCommand acommand = new CommonCommand("com.eactive.eai.agent.adapter.ReloadAdapterGroupCommand",
				adapterGroupUI.getAdptrbzwkgroupname());
		receive = agentUtilService.broadcast(acommand);
		
		if (receive.values().stream().anyMatch(value -> value == null || !"success".equals(value))) {
			status = "error";
		}
		
		result.put("status", status);
		result.put("message", receive.toString());
		return result;
		
	}

	public Map<String, String> deleteSync(AdapterGroupUI map, List<AdapterUI> list) throws Exception {
		Map<String, Object> receive = null;
		Map<String, String> result = new HashMap<>();
		String status = "success";

		for (AdapterUI adapter : list) {
			String prptyGroupName = adapter.getPrptygroupname();
			CommonCommand command = new CommonCommand("com.eactive.eai.agent.adapter.RemoveAdapterPropGroupCommand",
					prptyGroupName);
			agentUtilService.broadcast(command);
		}

		CommonCommand command = new CommonCommand("com.eactive.eai.agent.adapter.RemoveAdapterGroupCommand",
				map.getAdptrbzwkgroupname());
		receive = agentUtilService.broadcast(command);

		if (receive.values().stream().anyMatch(value -> value == null || !"success".equals(value))) {
			status = "error";
		}

		result.put("status", status);
		result.put("message", receive.toString());
		return result;
	}

	public List<AdapterGroupUI> selectSocketAdapterGroup(String adptrCdIn) {
		return adapterGroupService
				.findByAdptrcd(adptrCdIn)
				.stream()
				.map(adapterGroupUIMapper::toVo)
				.collect(Collectors.toList());
	}

	public CopyOnWriteArrayList<HashMap<String, String>> recover(String node) throws Exception {
		synchronized (obj) {
			CopyOnWriteArrayList<HashMap<String, String>> errors = new CopyOnWriteArrayList<>();
			List<Adapter> adapters = adapterGroupService.updateSingleNode(node);

			CountDownLatch stopLatch = new CountDownLatch(adapters.size());

			for (Adapter adapter : adapters) {
				String adapterGroupName = adapter.getId().getAdptrbzwkgroupname();
				String instanceName = adapter.getEaisevrinstncname();

				AdapterChangeThreadPool
						.getInstance()
						.getExecutor()
						.execute(new ReloadAction(errors, adapterGroupName, instanceName,
								DataSourceContextHolder.getDataSourceType(), stopLatch));
			}

			try {
				stopLatch.await();
			} catch (InterruptedException ex) {
				log.error(" error : ", ex);
				Thread.currentThread().interrupt();
			}

			return errors;
		}
	}

	public CopyOnWriteArrayList<HashMap<String, String>> change(String node) {
		synchronized (obj) {
			adapterGroupService.updateSingleNode(node);
			return new CopyOnWriteArrayList<HashMap<String, String>>();
		}
	}

	public boolean isTakeOverCheck() {
		// server 가 node로 구성 되어 있는지 확인
		long cnt = eaiServerService.selectTakeOverCount();
		return cnt > 0;
	}

	// SocketStatus화면에서 사용.
	public List selectListRealTimeGroup() {
		// 4.5 에는 해당 컬럼이 없다.
		return new ArrayList<>();
	}

	class ReloadAction implements Runnable {
		private CopyOnWriteArrayList<HashMap<String, String>> errors;
		private String adapterGroupName;
		private String instanceName;
		private DataSourceType dataSourceType;
		private CountDownLatch stopLatch;

		public ReloadAction(CopyOnWriteArrayList<HashMap<String, String>> errors, String adapterGroupName,
				String instanceName, DataSourceType dataSourceType, CountDownLatch stopLatch) {
			this.errors = errors;
			this.adapterGroupName = adapterGroupName;
			this.instanceName = instanceName;
			this.dataSourceType = dataSourceType;
			this.stopLatch = stopLatch;
		}

		public void run() {
			if (Thread.currentThread().getName().startsWith("pool-")) {
				Thread.currentThread().setName("AdapterChange" + "-" + Thread.currentThread().getName());
			}
			DataSourceContextHolder.setDataSourceType(dataSourceType);
			CommonCommand command = new CommonCommand("com.eactive.eai.agent.adapter.ReloadAdapterGroupCommand",
					adapterGroupName);
			HashMap<String, String> error = new HashMap<>();
			try {
				if (CommonUtil.getBooleanForSuccess(agentUtilService.broadcast(command, instanceName))) {
					;
				} else {
					error.put("adapterGroupName", adapterGroupName);
					error.put("instanceName", instanceName);
					error.put("errorMessage", "동기화 실패");
					errors.add(error);
				}
			} catch (Exception e) {
				error.put("adapterGroupName", adapterGroupName);
				error.put("instanceName", instanceName);
				error.put("errorMessage", e.getMessage());
				errors.add(error);
			} finally {
				stopLatch.countDown();
			}

		}

	}

	public List<Map<String, String>> selectAdapterPropInfo(Map<String, Object> map) throws Exception {
		return adapterPropLoader.findSnaAppcodeProps((String) map.get("searchPrptygroupName"));
	}

	public AdapterGroupUI importAdapterFromJson(MultipartFile file, boolean importDupliCheck) throws Exception {
		if (file.isEmpty()) {
			throw new IOException("File is empty");
		}

		try (InputStream inputStream = file.getInputStream()) {
			ObjectMapper objectMapper = new ObjectMapper();
			objectMapper.addMixIn(AdapterGroupUI.class, AdapterGroupUIMixIn.class);
			AdapterGroupUI adapterGroupUI = objectMapper.readValue(inputStream, AdapterGroupUI.class);
			
			String adapterGrpupName = adapterGroupUI.getAdptrbzwkgroupname();
			boolean adapterGroupExists = adapterGroupService.existsById(adapterGroupUI.getAdptrbzwkgroupname());
			
			if (adapterGroupExists) {
				
				if (importDupliCheck) {
					throw new IllegalStateException("AdapterGroup already exists [ " + adapterGrpupName + " ]" );
				} else {
					update(adapterGroupUI);
				}
				
			} else {
				insert(adapterGroupUI);
			}

//			if(!adapterGroupService.existsById(adapterGroupUI.getAdptrbzwkgroupname())){
//				insert(adapterGroupUI);
//			}else{
//				update(adapterGroupUI);
//			}

			return adapterGroupUI;
		}
	}

	public AdapterExportData exportAdapterToJson(String adapterGroupName) throws Exception {
		AdapterGroupUI adapterGroupUI = selectDetail(adapterGroupName);
		adapterGroupUI.setDInfo(null);
		Map<String, AdapterUI> adapterData = new HashMap<>();
		adapterGroupUI.setAdapterData(adapterData);

		for(AdapterUI adapterUI : adapterGroupUI.getAdapters()){
			adapterData.put(adapterUI.getAdptrbzwkname(), adapterUI);
		}
		
		adapterGroupUI.setAdapters(null);

		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.registerModule(new JavaTimeModule());
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
		ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter();
		byte[] jsonBytes = writer.writeValueAsBytes(adapterGroupUI);

		return new AdapterExportData(adapterGroupUI, jsonBytes);
	}
}
