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

import com.eactive.eai.adapter.Keys;
import com.eactive.eai.data.entity.onl.message.EAIMessageEntity;
import com.eactive.eai.data.entity.onl.message.QEAIMessageEntity;
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.messagekey.MessageKey;
import com.eactive.eai.data.entity.onl.stdmessage.QStandardMessageInfo;
import com.eactive.eai.data.entity.onl.stdmessage.StandardMessageInfo;
import com.eactive.eai.rms.data.entity.onl.djb.apistatus.QApiStatus;
import com.eactive.eai.rms.common.base.OnlBaseService;
import com.eactive.eai.rms.common.combo.ComboService;
import com.eactive.eai.rms.common.combo.ComboVo;
import com.eactive.eai.rms.common.util.PoiUtils;
import com.eactive.eai.rms.data.entity.onl.apim.apigroup.ApiGroupService;
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.data.entity.onl.messagekey.MessageKeyService;
import com.eactive.eai.rms.onl.common.exception.BizException;
import com.eactive.eai.rms.onl.manage.adapter.adapter.AdapterManService;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUI;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterPropUI;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterUI;
import com.eactive.eai.rms.onl.manage.rule.layout.LayoutManService;
import com.eactive.eai.rms.onl.manage.rule.layout.ui.LayoutItemUI;
import com.eactive.eai.rms.onl.manage.rule.layout.ui.LayoutUI;
import com.eactive.eai.rms.onl.manage.rule.transform2.Transform2Service;
import com.eactive.eai.rms.onl.manage.rule.transform2.ui.TransformItemUI;
import com.eactive.eai.rms.onl.manage.rule.transform2.ui.TransformUI;
import com.eactive.eai.rms.onl.manage.serviceinfo.stdmessage.*;
import com.eactive.eai.rms.onl.transaction.apim.mapping.ApiInterfaceUIMapper;
import com.eactive.eai.rms.onl.transaction.apim.mapping.BizCodeMapper;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiInterfaceExportData;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiInterfaceExportSetUI;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiInterfaceReloadSyncSet;
import com.eactive.eai.rms.onl.transaction.apim.ui.ApiInterfaceUI;
import com.eactive.eai.rms.onl.transaction.apim.vo.BizCodeVo;
import com.eactive.eai.rms.onl.transaction.apim.vo.RestOption;
import com.eactive.eai.transformer.transform.loader.TransformLoader;
import com.eactive.eai.transformer.util.Logger;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.querydsl.core.Tuple;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Transactional
@Repository
public class ApiInterfaceService extends OnlBaseService {

    private static final Logger logger = Logger.getLogger(ApiInterfaceService.class);

    public static final String STEMSG_SERVICE_URL_DELIMITER = "|";
    public static final String STEMSG_METHOD_DELIMITER = ":";
    public static final String STEMSG_SERVICE_HEADER_DELIMITER = "^";
    public static final String STEMSG_HEADER_SEPARATOR = ";";

    private final ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

    private final MessageKeyService messageKeyService;
    private final ApiInterfaceUIMapper apiInterfaceUIMapper;
    private final EAIMessageService eaiMessageQueryService;
    private final ServiceMessageService serviceMessageQueryService;
    private final StdMessageUIMapper stdMessageUIMapper;
    private final StandardMessageInfoQueryServiceForApi standardMessageInfoQueryService;
    private final AdapterManService adapterService;
    private final LayoutService layoutService;
    private final LayoutManService layoutManService;
    private final Transform2Service transform2Service;
    private final TransformLoader transformLoader;
    private final ComboService comboService;
    private final BizCodeMapper bizCodeMapper;
    private final StdMessageManService stdMessageManService;
    private final BizCodeQueryServiceForApi bizCodeQueryServiceForApi;
    private final ApiGroupService apiGroupService;


    @Autowired
    public ApiInterfaceService(MessageKeyService messageKeyService,
                               ApiInterfaceUIMapper apiInterfaceUIMapper,
                               EAIMessageService eaiMessageQueryService,
                               ServiceMessageService serviceMessageQueryService,
                               StdMessageUIMapper stdMessageUIMapper,
                               StandardMessageInfoQueryServiceForApi standardMessageInfoQueryService,
                               AdapterManService adapterService,
                               LayoutService layoutService,
                               LayoutManService layoutManService,
                               Transform2Service transform2Service,
                               TransformLoader transformLoader,
                               ComboService comboService,
                               BizCodeMapper bizCodeMapper,
                               StdMessageManService stdMessageManService,
                               BizCodeQueryServiceForApi bizCodeQueryServiceForApi,
                               ApiGroupService apiGroupService) {
        this.messageKeyService = messageKeyService;
        this.apiInterfaceUIMapper = apiInterfaceUIMapper;
        this.eaiMessageQueryService = eaiMessageQueryService;
        this.serviceMessageQueryService = serviceMessageQueryService;
        this.stdMessageUIMapper = stdMessageUIMapper;
        this.standardMessageInfoQueryService = standardMessageInfoQueryService;
        this.adapterService = adapterService;
        this.layoutService = layoutService;
        this.layoutManService = layoutManService;
        this.transform2Service = transform2Service;
        this.transformLoader = transformLoader;
        this.comboService = comboService;
        this.bizCodeMapper = bizCodeMapper;
        this.stdMessageManService = stdMessageManService;
        this.bizCodeQueryServiceForApi = bizCodeQueryServiceForApi;
        this.apiGroupService = apiGroupService;
    }

    public Page<ApiInterfaceUI> selectList(EAIMessageUISearch eaiMessageUISearch, Pageable pageable,String sortname,  String sortorder) {
    	
    	Page<Tuple> tuples = eaiMessageQueryService.selectList(eaiMessageUISearch, pageable, sortname, sortorder );

        return tuples.map(tuple -> {
            EAIMessageEntity eaiMessageEntity = tuple.get(QEAIMessageEntity.eAIMessageEntity);
            
            String apiFullPath = tuple.get(QStandardMessageInfo.standardMessageInfo.apifullpath);
            String bzwksvckeyname = tuple.get(QStandardMessageInfo.standardMessageInfo.bzwksvckeyname);
            String statusCode = tuple.get(QApiStatus.apiStatus.statusCode);

            ApiInterfaceUI apiInterfaceUI;
            if (eaiMessageEntity.getServiceMessages() != null && eaiMessageEntity.getServiceMessages().size() > 0) {
                apiInterfaceUI = apiInterfaceUIMapper.toVo(eaiMessageEntity, eaiMessageEntity.getServiceMessages());
            } else {
                apiInterfaceUI = apiInterfaceUIMapper.toVo(eaiMessageEntity);
            }

            apiInterfaceUI.setApiFullPath(apiFullPath);
            apiInterfaceUI.setBzwksvckeyname(bzwksvckeyname);
            apiInterfaceUI.setStatusCode(statusCode);

            return apiInterfaceUI;
        });
    }

    public HashMap selectCombo(String userId) throws Exception {

        List<BizCodeVo> voList = bizCodeMapper.toVoList(bizCodeQueryServiceForApi.selectUserBizCodes(userId));
        // 전체 목록수 얻기
        HashMap map = new HashMap();
        map.put("bizList", voList);

        return map;
    }

    public ApiInterfaceUI selectDetail(String eaiSvcName) throws Exception {
        EAIMessageEntity eaiMessageEntity = eaiMessageQueryService.findById(eaiSvcName).get();
        ApiInterfaceUI apiInterfaceUI;
        List<ServiceMessageEntity> serviceMessageEntityList = eaiMessageEntity.getServiceMessages();
        if (serviceMessageEntityList != null && serviceMessageEntityList.size() > 0) {
            apiInterfaceUI = apiInterfaceUIMapper.toVo(eaiMessageEntity, serviceMessageEntityList);
        } else {
            apiInterfaceUI = apiInterfaceUIMapper.toVo(eaiMessageEntity);
        }

        // 레이아웃 이름 설정
        setLayoutNames(apiInterfaceUI, eaiMessageEntity.getEaibzwkdstcd(), eaiSvcName);
        String transformYn = "N";
        String errTransformYn = "N";  // 추가

        if (serviceMessageEntityList != null && serviceMessageEntityList.size() > 0) {
            ServiceMessageEntity serviceMessageEntity = serviceMessageEntityList.get(0);
            String requestTranform = serviceMessageEntity.getChngmsgidname();
            apiInterfaceUI.setRequestTransform(requestTranform);
            String responseTranform = serviceMessageEntity.getBascrspnschngmsgidname();
            apiInterfaceUI.setResponseTransform(responseTranform);
            String errResponseTransform = serviceMessageEntity.getErrrspnschngmsgidname();  // 추가
            apiInterfaceUI.setErrResponseTransform(errResponseTransform);  // 추가

            if ("1".equals(serviceMessageEntity.getChngeonot()) || "1".equals(serviceMessageEntity.getBascrspnschngeonot())) {// 요청이나 응답 변환 여부
                transformYn = "Y";
            }
            if ("1".equals(serviceMessageEntity.getErrrspnschngeonot())) {  // 추가
                errTransformYn = "Y";
            }

            String restOptionStr = serviceMessageEntity.getRestoption();

            if (StringUtils.isNotBlank(restOptionStr)) {
                ObjectMapper mapper = new ObjectMapper();
                RestOption restOption = mapper.readValue(restOptionStr, RestOption.class);

                apiInterfaceUI.setOutboundHttpMethod(restOption.getMethod());
                apiInterfaceUI.setOutboundRestPath(restOption.getExtraPath());
                if (StringUtils.isNotBlank(restOption.getContentType())) {
                    apiInterfaceUI.setRestContentType(restOption.getContentType());
                }
            }
        }

        if("asyncSync".equals(apiInterfaceUI.getSyncAsyncType()) && serviceMessageEntityList != null && serviceMessageEntityList.size() > 1){
            ServiceMessageEntity extraServiceMessageEntity = serviceMessageEntityList.get(1);
            apiInterfaceUI.setFromResponseAdapter(extraServiceMessageEntity.getPsvsysadptrbzwkgroupname());
            apiInterfaceUI.setToutVal2(extraServiceMessageEntity.getToutval());
            String restOptionStr = extraServiceMessageEntity.getRestoption();

            if(StringUtils.isNotBlank(restOptionStr)) {
                ObjectMapper mapper = new ObjectMapper();
                RestOption restOption = mapper.readValue(restOptionStr, RestOption.class);

                apiInterfaceUI.setInboundResponseHttpMethod(restOption.getMethod());
                apiInterfaceUI.setInboundResponseRestPath(restOption.getExtraPath());
                apiInterfaceUI.setInboundResponseStandardMessageItems(convertMapToStdMessageItems(restOption.getStandardCommonFields()));
            }
        }
        
        if("syncAsync".equals(apiInterfaceUI.getSyncAsyncType()) && serviceMessageEntityList != null && serviceMessageEntityList.size() > 1){
        	ServiceMessageEntity extraServiceMessageEntity = serviceMessageEntityList.get(1);
        	apiInterfaceUI.setToResponseAdapter(extraServiceMessageEntity.getPsvsysadptrbzwkgroupname());
        	String restOptionStr = extraServiceMessageEntity.getRestoption();
        	
        	if(StringUtils.isNotBlank(restOptionStr)) {
        		ObjectMapper mapper = new ObjectMapper();
        		RestOption restOption = mapper.readValue(restOptionStr, RestOption.class);
        		
        		apiInterfaceUI.setOutboundResponseHttpMethod(restOption.getMethod());
        		apiInterfaceUI.setOutboundResponseRestPath(restOption.getExtraPath());
        	}
        }

        apiInterfaceUI.setTransformYn(transformYn);
        apiInterfaceUI.setErrTransformYn(errTransformYn);  // 추가

        Optional<StandardMessageInfo> optional = standardMessageInfoQueryService.findByEaiSvcName(eaiSvcName);
        if (optional.isPresent()) {
            StandardMessageInfo standardMessageInfo = optional.get();
            StdMessageUI stdMessageUI = stdMessageUIMapper.toVo(standardMessageInfo);

            String stdKey = stdMessageUI.getBzwkSvcKeyName();
            String inboundRestPath;
            if (StringUtils.contains(stdKey, STEMSG_SERVICE_HEADER_DELIMITER)) {
            	inboundRestPath = StringUtils.substringBetween(stdKey, STEMSG_SERVICE_URL_DELIMITER, STEMSG_SERVICE_HEADER_DELIMITER);
            } else {
            	inboundRestPath = StringUtils.substringAfter(stdKey, STEMSG_SERVICE_URL_DELIMITER);
            }
            apiInterfaceUI.setInboundRestPath(inboundRestPath);
            apiInterfaceUI.setApiFullPath(standardMessageInfo.getApifullpath());

            apiInterfaceUI.setStandardMessageItems(stdMessageUI.getStandardMessageItems());

            String inboundHttpMethod;
            if (StringUtils.contains(stdKey, STEMSG_SERVICE_HEADER_DELIMITER)) {
            	if (StringUtils.contains(stdKey, STEMSG_SERVICE_URL_DELIMITER)) {
            		inboundHttpMethod = StringUtils.substringBetween(stdKey, STEMSG_METHOD_DELIMITER, STEMSG_SERVICE_URL_DELIMITER);
            	} else {            		
            		inboundHttpMethod = StringUtils.substringBetween(stdKey, STEMSG_METHOD_DELIMITER, STEMSG_SERVICE_HEADER_DELIMITER);
            	}
                String headerRoutingValueString = StringUtils.substringAfter(stdKey, STEMSG_SERVICE_HEADER_DELIMITER);

                List<String> headerValues = new ArrayList<>();
                String[] values = headerRoutingValueString.split(ApiInterfaceService.STEMSG_HEADER_SEPARATOR);

                for (String value : values) {
                    headerValues.add(value);
                }
                apiInterfaceUI.setHeaderValues(headerValues);
            } else {
                inboundHttpMethod = StringUtils.substringBetween(stdKey, STEMSG_METHOD_DELIMITER, STEMSG_SERVICE_URL_DELIMITER);
            }

            apiInterfaceUI.setInboundHttpMethod(inboundHttpMethod == null ? "" : inboundHttpMethod.toLowerCase());
        }

        return apiInterfaceUI;
    }

    private void setLayoutNames(ApiInterfaceUI apiInterfaceUI, String bzcd, String eaiSvcName) {
        String[] types = {"SRCS", "TGTS", "TGTR", "SRCR", "SRCE", "TGTE"}; // SRCE, TGTE 추가
        for (String type : types) {
            String layoutName = bzcd + "_" + eaiSvcName + "_" + type;
            boolean hasLayout = layoutService.findById(layoutName).isPresent();
            if (hasLayout) {
                switch (type) {
                    case "SRCS":
                        apiInterfaceUI.setInboundRequestLayout(layoutName);
                        break;
                    case "TGTS":
                        apiInterfaceUI.setOutboundRequestLayout(layoutName);
                        break;
                    case "TGTR":
                        apiInterfaceUI.setOutboundResponseLayout(layoutName);
                        break;
                    case "SRCR":
                        apiInterfaceUI.setInboundResponseLayout(layoutName);
                        break;
                    case "SRCE":
                        apiInterfaceUI.setInboundErrResponseLayout(layoutName);
                        break;
                    case "TGTE":
                        apiInterfaceUI.setOutboundErrResponseLayout(layoutName);
                        break;
                }
            }
        }
    }

    public HashMap selectDetailCombo(String userId) throws Exception {
        HashMap paramMap = new HashMap();
        paramMap.put("userId", userId);

        //업무구분
        List<BizCodeVo> listBiz = bizCodeMapper.toVoList(bizCodeQueryServiceForApi.selectUserBizCodes(userId));
        //당타발구분
        List<ComboVo> listIo = comboService.getFromCode("IO_TYPE");
        //전문요청구분
        List listReqRes = comboService.getFromCode("REQRES_TYPE");
        //송수신방법
        List listSyncAsync = comboService.getFromCode("SYNCASYNC_TYPE");
        //거래흐름(INBOUND);
        List<AdapterGroupUI> listFrom = adapterService.selectListComboForAdptrIoDstcd("IN");
        //거래흐름(OUTBOUND);
        List<AdapterGroupUI> listTo = adapterService.selectListComboForAdptrIoDstcd("OU");

        List<ExtendedColumnDefinitionUI> stdCommonFieldDefinitions = stdMessageManService.selectColumnList();

        Map<String, List<String>> fromAdapterMessageKeyMap = new HashMap<>();

        listFrom.forEach(adapterGroup -> {
            String adapterGroupName = adapterGroup.getAdptrbzwkgroupname();
            List<String> messageKeyGroup = getFirstBzwkfldnameListsByGroup(adapterGroupName);
            if (messageKeyGroup != null && messageKeyGroup.size() > 0) {
                fromAdapterMessageKeyMap.put(adapterGroupName, messageKeyGroup);
            }
        });

        HashMap map = new HashMap();

        map.put("bizList", listBiz);
        map.put("ioList", listIo);
        map.put("reqResList", listReqRes);
        map.put("syncAsyncList", listSyncAsync);
        map.put("fromList", listFrom);
        map.put("fromAdapterMessageKeyMap", fromAdapterMessageKeyMap);
        map.put("stdCommonFieldDefinitions", stdCommonFieldDefinitions);
        map.put("toList", listTo);

        return map;
    }

    public HashMap getHttpAdapterInfo(String adapterGroupName) throws Exception {
        AdapterGroupUI adapterGroup = adapterService.selectDetail(adapterGroupName);
        String adptriodstcd = adapterGroup.getAdptriodstcd();
        StringBuilder urlPathBuilder = new StringBuilder();

        for (AdapterUI adapter : adapterGroup.getAdapters()) {
            String url = null;
            for (AdapterPropUI prop : adapter.getAdapterPropGroup().getAdapterProps()) {
                String propName = "I".equals(adptriodstcd) ? "API_PATH" : "URL";
                if (propName.equals(prop.getPrptyname())) {
                    url = prop.getPrpty2val();
                    break;
                }
            }

            if (url != null) {
                if (urlPathBuilder.length() > 100) {
                    urlPathBuilder.append("...");
                    break;
                }
                if (urlPathBuilder.length() > 0) {
                    urlPathBuilder.append(", "); // 기존에 문자열이 있다면 쉼표 추가
                }
                urlPathBuilder.append(url); // 새로운 URL 부분 추가
            }
        }
        String urlPath = urlPathBuilder.toString(); // 최종 문자열 얻기

        HashMap<String, String> resultMap = new HashMap<>();
        resultMap.put("urlPath", urlPath);
        resultMap.put("adapterMessageGroupType", adapterGroup.getAdptrmsgptrncd());
        return resultMap;
    }

    public AdapterGroupUI selectAdapterGroupDetail(String adapterGroupName) throws Exception {
        return adapterService.selectDetail(adapterGroupName);
    }

    public void update(ApiInterfaceUI apiInterfaceUI) throws Exception {
        String eaiSvcName = apiInterfaceUI.getEaiSvcName();

        EAIMessageEntity eaiMessageEntity = eaiMessageQueryService.getById(eaiSvcName);
        apiInterfaceUIMapper.updateEaiMessageEntity(apiInterfaceUI, eaiMessageEntity);
        eaiMessageQueryService.save(eaiMessageEntity);

        List<ServiceMessageEntity> serviceMessageEntityList = eaiMessageEntity.getServiceMessages();

        if (eaiMessageEntity.getServiceMessages().size() > 0) {
            ServiceMessageEntity serviceMessageEntity = eaiMessageEntity.getServiceMessages().get(0);
            apiInterfaceUIMapper.updateServiceMessageEntity(apiInterfaceUI, serviceMessageEntity);
        } else {
            ServiceMessageEntity serviceMessageEntity = apiInterfaceUIMapper.toServiceMessageEntity(apiInterfaceUI);
            serviceMessageEntityList.add(serviceMessageEntity);
        }

        setServiceMessageRoutingInfo(apiInterfaceUI, eaiMessageEntity, serviceMessageEntityList);

        for (ServiceMessageEntity serviceMessageEntity : serviceMessageEntityList) {
            serviceMessageQueryService.save(serviceMessageEntity);
        }

        AdapterGroupUI adapterGroup = adapterService.selectDetail(apiInterfaceUI.getFromAdapter());
        if (!Keys.IF_STANDARD.equals(adapterGroup.getAdptrmsgptrncd())) {
            mergeStdMessage(adapterGroup, apiInterfaceUI);
        }
    }

    private String getOutboundProcessorName(AdapterGroupUI outboundAdapter) throws Exception {
        if (outboundAdapter == null) {
            throw new Exception("아웃바운드 어댑터 오류입니다.");
        }

        String outbndRoutName;
        switch (outboundAdapter.getAdptrcd()) {
            case "HTT":
                outbndRoutName = "HTTPOUT";
                break;
            case "NET":
                outbndRoutName = "SOCKETOUT";
                break;
            case "APP":
                outbndRoutName = "APPOUT";
                break;
            default:
                outbndRoutName = "RESTOUT";
                break;
        }
        return outbndRoutName;
    }

    private void setRouteInfo(ApiInterfaceUI vo, String httpMethod, String restPath, String adapterGroupName, ServiceMessageEntity serviceMessageEntity, List<StdMessageItemUI> standardMessageItems) throws Exception {
        AdapterGroupUI outboundAdapter = adapterService.selectDetail(adapterGroupName);
        String outboundRoutName = getOutboundProcessorName(outboundAdapter);
        serviceMessageEntity.setOutbndroutname(outboundRoutName);

        if(Keys.IF_STANDARD.equals(outboundAdapter.getAdptrmsgptrncd())) {
            String routeOption = makeStandardMessageRouteOption(standardMessageItems);
            serviceMessageEntity.setRestoption(routeOption);
        } else if ("RESTOUT".equals(outboundRoutName) && !Keys.IF_STANDARD.equals(outboundAdapter.getAdptrmsgptrncd())) {
            String routeOption =  makeRestRouteOption(httpMethod, restPath, vo.getRestContentType());
            serviceMessageEntity.setRestoption(routeOption);
        }
    }

    public void setServiceMessageRoutingInfo(ApiInterfaceUI vo, EAIMessageEntity eaiMessageEntity, List<ServiceMessageEntity> serviceMessageEntityList) throws Exception {
        // 기본 요청 ServiceMessageEntity 설정
        ServiceMessageEntity requestEntity = serviceMessageEntityList.get(0);
        
        if ("async".equals(vo.getSyncAsyncType()) || "syncAsync".equals(vo.getSyncAsyncType())) {
            requestEntity.setPsvintfacdsticname("ASYN");
        }

        setRouteInfo(vo, vo.getOutboundHttpMethod(), vo.getOutboundRestPath(), vo.getToAdapter(), requestEntity, vo.getStandardMessageItems());

        // asyncSync 타입인 경우 응답용 ServiceMessageEntity 추가 처리
        if ("asyncSync".equals(vo.getSyncAsyncType())) {
            ServiceMessageEntity responseEntity;

            // 응답용 ServiceMessageEntity가 없으면 새로 생성
            if (serviceMessageEntityList.size() < 2) {
                responseEntity = new ServiceMessageEntity();
                responseEntity.setId(new ServiceMessageEntityId(vo.getEaiSvcName(), 2));
                responseEntity.setEaimessage(eaiMessageEntity);
                serviceMessageEntityList.add(responseEntity);
            } else {
                responseEntity = serviceMessageEntityList.get(1);
            }

            // 응답용 ServiceMessageEntity 설정
            responseEntity.setPsvsysadptrbzwkgroupname(vo.getFromResponseAdapter());
            responseEntity.setPsvintfacdsticname("ASYN");
            responseEntity.setToutval(vo.getToutVal2());

            // 응답용 REST 정보 설정
            AdapterGroupUI responseAdapter = adapterService.selectDetail(vo.getFromResponseAdapter());
            String responseRoutName = getOutboundProcessorName(responseAdapter);
            responseEntity.setOutbndroutname(responseRoutName);

            setRouteInfo(vo, vo.getInboundResponseHttpMethod(), vo.getInboundResponseRestPath(), vo.getFromResponseAdapter(), responseEntity, vo.getInboundResponseStandardMessageItems());
        }
        
        // syncAsync 타입인 경우 응답용 ServiceMessageEntity 추가 처리
        if ("syncAsync".equals(vo.getSyncAsyncType())) {
        	ServiceMessageEntity responseEntity;
        	
        	// ServiceMessageEntity가 없으면 새로 생성
        	if (serviceMessageEntityList.size() < 2) {
        		responseEntity = new ServiceMessageEntity();
        		responseEntity.setId(new ServiceMessageEntityId(vo.getEaiSvcName(), 2));
        		responseEntity.setEaimessage(eaiMessageEntity);
        		serviceMessageEntityList.add(responseEntity);
        	} else {
        		responseEntity = serviceMessageEntityList.get(1);
        	}
        	
        	// ServiceMessageEntity 설정
        	responseEntity.setPsvsysadptrbzwkgroupname(vo.getToResponseAdapter());
        	responseEntity.setPsvintfacdsticname("ASYN");
        	responseEntity.setToutval(vo.getToutVal());
        	
        	// REST 정보 설정
        	AdapterGroupUI responseAdapter = adapterService.selectDetail(vo.getToResponseAdapter());
        	String responseRoutName = getOutboundProcessorName(responseAdapter);
        	responseEntity.setOutbndroutname(responseRoutName);
        	
        	setRouteInfo(vo, vo.getOutboundResponseHttpMethod(), vo.getOutboundResponseRestPath(), vo.getToResponseAdapter(), responseEntity, vo.getStandardMessageItems());
        }
    }

    public static List<StdMessageItemUI> convertMapToStdMessageItems(Map<String, String> standardCommonFields) {
        if (standardCommonFields == null) {
            return new ArrayList<>();
        }

        return standardCommonFields.entrySet().stream()
                .map(entry -> {
                    StdMessageItemUI stdMessageItemUI = new StdMessageItemUI();
                    stdMessageItemUI.setColumnname(entry.getKey());
                    stdMessageItemUI.setColumnvalue(entry.getValue());
                    return stdMessageItemUI;
                })
                .collect(Collectors.toList());
    }

    public String makeStandardMessageRouteOption(List<StdMessageItemUI> standardMessageItems) {
        try {
            RestOption restOption = new RestOption();
            Map<String, String> standardCommonFields = new HashMap<>();

            for (StdMessageItemUI stdMessageItemUI : standardMessageItems) {
                standardCommonFields.put(stdMessageItemUI.getColumnname(), stdMessageItemUI.getColumnvalue());
            }

            restOption.setStandardCommonFields(standardCommonFields);
            return objectMapper.writeValueAsString(restOption);
        } catch (Exception e) {
            throw new RuntimeException("표준전문 공통부 생성을 실패했습니다", e);
        }
    }

    public String makeRestRouteOption(String outboundHttpMethod, String outboundRestPath, String contentType) {
        try {
            RestOption restOption = new RestOption();
            restOption.setMethod(outboundHttpMethod);
            restOption.setExtraPath(outboundRestPath);

            String pathType = outboundRestPath.matches(".*\\{.+\\}.*") ? "variableUrlRequest" : "simpleRequest";
            restOption.setType(pathType);

            if (StringUtils.isNotBlank(contentType)) {
                restOption.setContentType(contentType);
            }

            return objectMapper.writeValueAsString(restOption);
        } catch (Exception e) {
            throw new RuntimeException("Failed to create REST option", e);
        }
    }


    public void insert(ApiInterfaceUI apiInterfaceUI) throws Exception {
        String eaiSvcName = apiInterfaceUI.getEaiSvcName();

        Optional<EAIMessageEntity> optional = eaiMessageQueryService.findById(eaiSvcName);
        if (optional.isPresent()) {
            String bzwkDstcd = optional.get().getEaibzwkdstcd();
            throw new BizException("업무명[" + bzwkDstcd + "]로 이미 등록된 인터페이스입니다.\n조회가 되지 않을 시 담당자에게 문의바랍니다.");
        }

        EAIMessageEntity eaiMessageEntity = apiInterfaceUIMapper.toEaiMessageEntity(apiInterfaceUI);
        ServiceMessageEntity serviceMessageEntity = apiInterfaceUIMapper.toServiceMessageEntity(apiInterfaceUI);
        serviceMessageEntity.setEaimessage(eaiMessageEntity);
        List<ServiceMessageEntity> serviceMessageEntityList = new ArrayList<>();
        serviceMessageEntityList.add(serviceMessageEntity);
        eaiMessageEntity.setServiceMessages(serviceMessageEntityList);

        setServiceMessageRoutingInfo(apiInterfaceUI, eaiMessageEntity, serviceMessageEntityList);

        eaiMessageQueryService.save(eaiMessageEntity);

        AdapterGroupUI adapterGroup = adapterService.selectDetail(apiInterfaceUI.getFromAdapter());
        if (!Keys.IF_STANDARD.equals(adapterGroup.getAdptrmsgptrncd())) {
            mergeStdMessage(adapterGroup, apiInterfaceUI);
        }
    }


    public String mergeStdMessage(AdapterGroupUI adapterGroup, ApiInterfaceUI vo) throws Exception {
        logger.info("내부 표준 전문 INSERT");
        // 내부 표준 전문 INSERT
        // 비표준에서 표준으로 가는 경우 표준으로 전환하기 위한 정보 이므로 내부로 갈 때만 처리

        String eaiServiceName = vo.getEaiSvcName();
        
        // 25.11.25 HS04 APIFULLPATH 추가.
        List<AdapterUI> adapters = adapterGroup.getAdapters();
        String inboundHttpMethod = vo.getInboundHttpMethod().toUpperCase();
        
        for (AdapterUI adapter : adapters) {
        	
        	// propGroup NULL 체크
        	if(adapter.getAdapterPropGroup() == null) continue;
        	
        	List<AdapterPropUI> props = adapter.getAdapterPropGroup().getAdapterProps();
        	
        	if (props != null) {
        		for (AdapterPropUI prop : props) {
        			if("API_PATH".equals(prop.getPrptyname())) {
        				
        				String fullPath = inboundHttpMethod + "|" + prop.getPrpty2val() + "/" + vo.getInboundRestPath();
        				vo.setApiFullPath(fullPath);
        				
        	        	// HS04 AndEaiSvcNameNot : 업데이트일 때 자신의 fullpath 는 제외함
        	        	standardMessageInfoQueryService.existsByApiFullPathAndEaiSvcNameNot(vo.getApiFullPath(), eaiServiceName);
        				
        				break;
        			}
        		}
        	}
        }
        
        // HTTP 어댑터도 등록 가능하도록 수정.
        if (!"RST".equals(adapterGroup.getAdptrcd())&&!"HTT".equals(adapterGroup.getAdptrcd())) {
            throw new RuntimeException("인바운드 어댑터가 타입이 REST/HTTP가 아닙니다. 타입을 확인하세요");
        }

        StandardMessageInfo standardMessageInfo = apiInterfaceUIMapper.toStandardMessageInfo(vo);

        standardMessageInfoQueryService.deleteByEaiSvcName(eaiServiceName);
        standardMessageInfoQueryService.save(standardMessageInfo);

        return standardMessageInfo.getBzwksvckeyname();
    }


    public String delete(ApiInterfaceUI apiInterfaceUI) throws Exception {
        String bzwksvckeyname = null;
        String eaiSvcName = apiInterfaceUI.getEaiSvcName();

        apiGroupService.deleteApiGroupApiByApiId(eaiSvcName);

        Optional<StandardMessageInfo> optionalStandardMessageInfo = standardMessageInfoQueryService.findByEaiSvcName(apiInterfaceUI.getEaiSvcName());
        if (optionalStandardMessageInfo.isPresent()) {
            StandardMessageInfo standardMessageInfo = optionalStandardMessageInfo.get();
            bzwksvckeyname = standardMessageInfo.getBzwksvckeyname();
            standardMessageInfoQueryService.deleteByEaiSvcName(eaiSvcName);
        }

        if (StringUtils.isNotBlank(eaiSvcName)) {
            eaiMessageQueryService.deleteById(eaiSvcName);
        }
        deleteIfNotBlank(apiInterfaceUI.getRequestTransform(), transformLoader::deleteById);
        deleteIfNotBlank(apiInterfaceUI.getResponseTransform(), transformLoader::deleteById);
        deleteIfNotBlank(apiInterfaceUI.getInboundRequestLayout(), layoutService::deleteById);
        deleteIfNotBlank(apiInterfaceUI.getOutboundRequestLayout(), layoutService::deleteById);
        deleteIfNotBlank(apiInterfaceUI.getInboundResponseLayout(), layoutService::deleteById);
        deleteIfNotBlank(apiInterfaceUI.getOutboundResponseLayout(), layoutService::deleteById);
        deleteIfNotBlank(apiInterfaceUI.getInboundErrResponseLayout(), layoutService::deleteById);
        deleteIfNotBlank(apiInterfaceUI.getOutboundErrResponseLayout(), layoutService::deleteById);

        return bzwksvckeyname;
    }


    private void deleteIfNotBlank(String value, Consumer<String> deleteFunction) throws Exception {
        if (StringUtils.isNotBlank(value)) {
            deleteFunction.accept(value);
        }
    }

    protected List<String> getFirstBzwkfldnameListsByGroup(String adapterGroupName) {
        List<List<String>> messageKeyGroupList = getBzwkfldnameListsByGroup(adapterGroupName);
        if (messageKeyGroupList == null || messageKeyGroupList.size() == 0) {
            return null;
        }

        return messageKeyGroupList.get(0);
    }

    protected List<List<String>> getBzwkfldnameListsByGroup(String adapterGroupName) {
        List<MessageKey> messageKeys = messageKeyService.findByAdptrBzwkGroupNameAndIoDstcd(adapterGroupName, "I", true);
        // groupinorno 별로 MessageKey 객체들을 그룹화합니다.
        Map<Integer, List<MessageKey>> groupedByGroupNo = messageKeys.stream()
                .collect(Collectors.groupingBy(key -> key.getId().getGroupinorno()));

        List<List<String>> headerNamesLists = new ArrayList<>();
        // 각 그룹별로 bzwkfldname 값을 추출하여 리스트로 만듭니다.
        for (Map.Entry<Integer, List<MessageKey>> entry : groupedByGroupNo.entrySet()) {
            List<MessageKey> groupedKeys = entry.getValue();
            List<String> headerNames = new ArrayList<>();

            for (MessageKey key : groupedKeys) {
                headerNames.add(key.getBzwkfldname());
            }

            headerNamesLists.add(headerNames);
        }

        return headerNamesLists;
    }


    public ApiInterfaceExportData exportInterfaceToExcel(String interfaceId) throws Exception {
        ApiInterfaceUI apiInterfaceUI = selectDetail(interfaceId);

        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("인터페이스");

            // 먼저 LayoutUI 정보를 시트 상단에 배치
            createInterfaceForm(apiInterfaceUI, sheet, workbook);

            if ("Y".equals(apiInterfaceUI.getTransformYn())) {
                layoutManService.addLayoutExcelSheet(apiInterfaceUI.getInboundRequestLayout(), workbook, "인바운드 요청 레이아웃");
                layoutManService.addLayoutExcelSheet(apiInterfaceUI.getOutboundRequestLayout(), workbook, "아웃바운드 요청 레이아웃");
                transform2Service.addTransformExcelSheet(apiInterfaceUI.getRequestTransform(), workbook, "요청 변환");
                layoutManService.addLayoutExcelSheet(apiInterfaceUI.getOutboundResponseLayout(), workbook, "아웃바운드 응답 레이아웃");
                layoutManService.addLayoutExcelSheet(apiInterfaceUI.getInboundResponseLayout(), workbook, "인바운드 응답 레이아웃");
                transform2Service.addTransformExcelSheet(apiInterfaceUI.getResponseTransform(), workbook, "응답 변환");
            }

            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                workbook.write(byteArrayOutputStream);
                return new ApiInterfaceExportData(apiInterfaceUI, byteArrayOutputStream.toByteArray());
            }
        }
    }

    private void createInterfaceForm(ApiInterfaceUI apiInterfaceUI, Sheet sheet, Workbook workbook) {
        int height = 25;
        // 헤더 스타일 생성 및 설정
        CellStyle headerStyle = PoiUtils.createHeaderCellStyle(workbook);
        // 텍스트 스타일 생성 및 설정
        CellStyle textStyle = PoiUtils.createTextCellStyle(workbook);

        CellStyle titleStyle = PoiUtils.createTitleCellStyle(workbook);

        int ci = 0;
        sheet.setColumnWidth(ci++, 200 * 30);
        sheet.setColumnWidth(ci++, 300 * 30);
        sheet.setColumnWidth(ci++, 200 * 30);
        sheet.setColumnWidth(ci++, 300 * 30);
        sheet.setColumnWidth(ci++, 200 * 30);
        sheet.setColumnWidth(ci++, 200 * 30);
        sheet.setColumnWidth(ci++, 300 * 30);
        sheet.setColumnWidth(ci, 300 * 30);

        int rowNum = 0;
        rowNum = addTitle(sheet, rowNum, titleStyle, "기본 설정");

        Row row = sheet.createRow(rowNum++);
        row.setHeightInPoints(height); // 행 높이를 20 포인트로 설정
        int cellIndex = 0; // 초기 셀 인덱스
        createCell(row, cellIndex++, headerStyle, "타입");
        createCell(row, cellIndex++, textStyle, "INTERFACE");
        createCell(row, cellIndex++, headerStyle, "업무구분");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getEaiBzwkDstcd());
        createCell(row, cellIndex++, headerStyle, "인터페이스 ID");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getEaiSvcName());
        row = sheet.createRow(rowNum++);
        row.setHeightInPoints(height); // 행 높이를 20 포인트로 설정
        cellIndex = 0; // 초기 셀 인덱스
        createCell(row, cellIndex++, headerStyle, "설명");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getEaiSvcDesc());
        createCell(row, cellIndex++, headerStyle, "변환여부(Y/N)");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getTransformYn());
        createCell(row, cellIndex++, headerStyle, "Sync/Async 타입");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getSyncAsyncType());

        rowNum = addTitle(sheet, ++rowNum, titleStyle, "어댑터 설정");
        row = sheet.createRow(rowNum++);
        row.setHeightInPoints(height); // 행 높이를 20 포인트로 설정
        cellIndex = 0; // 초기 셀 인덱스
        createCell(row, cellIndex++, headerStyle, "인바운드 어댑터");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getFromAdapter());
        createCell(row, cellIndex++, headerStyle, "아웃바운드 어댑터");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getToAdapter());
        
        //SYNC TYPE 에 따라 추가정보 생성
        String syncAsyncType = apiInterfaceUI.getSyncAsyncType();
        
        if (syncAsyncType.equals("asyncSync") || syncAsyncType.equals("syncAsync")) {
        	
        	if (apiInterfaceUI.getSyncAsyncType().equals("asyncSync")) {
        		createCell(row, cellIndex++, headerStyle, "INBOUND (ASYNC) 응답 어댑터");
        		createCell(row, cellIndex, textStyle, apiInterfaceUI.getFromResponseAdapter());
        	} else {
        		createCell(row, cellIndex++, headerStyle, "OUTBOUND (ASYNC) 응답 어댑터");
        		createCell(row, cellIndex, textStyle, apiInterfaceUI.getToResponseAdapter());        	
        	}
        	
        }
        
        rowNum = addTitle(sheet, ++rowNum, titleStyle, "REST 설정");
        row = sheet.createRow(rowNum++);
        row.setHeightInPoints(height); // 행 높이를 20 포인트로 설정
        cellIndex = 0; // 초기 셀 인덱스
        createCell(row, cellIndex++, headerStyle, "수신 메소드");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getInboundHttpMethod());
        createCell(row, cellIndex++, headerStyle, "수신 PATH(URL)");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getInboundRestPath());
        
        if(apiInterfaceUI.getSyncAsyncType().equals("asyncSync")) {
        	createCell(row, cellIndex++, headerStyle, "응답 송신 메소드");
            createCell(row, cellIndex++, textStyle, apiInterfaceUI.getInboundResponseHttpMethod());
            createCell(row, cellIndex++, headerStyle, "응답 송신 REST PATH(URL)");
            createCell(row, cellIndex, textStyle, apiInterfaceUI.getInboundResponseRestPath());
        }
        
        row = sheet.createRow(rowNum++);
        row.setHeightInPoints(height); // 행 높이를 20 포인트로 설정
        cellIndex = 0; // 초기 셀 인덱스
        createCell(row, cellIndex++, headerStyle, "송신 메소드");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getOutboundHttpMethod());
        createCell(row, cellIndex++, headerStyle, "송신 PATH(URL)");
        createCell(row, cellIndex++, textStyle, apiInterfaceUI.getOutboundRestPath());
        
        if(apiInterfaceUI.getSyncAsyncType().equals("syncAsync")) {        
        	createCell(row, cellIndex++, headerStyle, "응답 수신 메소드");
            createCell(row, cellIndex++, textStyle, apiInterfaceUI.getOutboundResponseHttpMethod());
            createCell(row, cellIndex++, headerStyle, "송신 REST PATH(URL)");
            createCell(row, cellIndex, textStyle, apiInterfaceUI.getOutboundResponseRestPath());    	
        }


        if ("Y".equals(apiInterfaceUI.getTransformYn())) {
            rowNum = addTitle(sheet, ++rowNum, titleStyle, "변환 설정");
            row = sheet.createRow(rowNum++);
            row.setHeightInPoints(height); // 행 높이를 20 포인트로 설정
            cellIndex = 0; // 초기 셀 인덱스
            createCell(row, cellIndex++, headerStyle, "인바운드 요청 레이아웃");
            createCell(row, cellIndex++, textStyle, apiInterfaceUI.getInboundRequestLayout());
            createCell(row, cellIndex++, headerStyle, "요청 변환");
            createCell(row, cellIndex++, textStyle, apiInterfaceUI.getRequestTransform());
            createCell(row, cellIndex++, headerStyle, "아웃바운드 요청 레이아웃");
            createCell(row, cellIndex, textStyle, apiInterfaceUI.getOutboundRequestLayout());
            row = sheet.createRow(rowNum);
            row.setHeightInPoints(height); // 행 높이를 20 포인트로 설정
            cellIndex = 0; // 초기 셀 인덱스
            createCell(row, cellIndex++, headerStyle, "인바운드 응답 레이아웃");
            createCell(row, cellIndex++, textStyle, apiInterfaceUI.getInboundResponseLayout());
            createCell(row, cellIndex++, headerStyle, "응답 변환");
            createCell(row, cellIndex++, textStyle, apiInterfaceUI.getResponseTransform());
            createCell(row, cellIndex++, headerStyle, "아웃바운드 응답 레이아웃");
            createCell(row, cellIndex, textStyle, apiInterfaceUI.getOutboundResponseLayout());
        }
    }

    private int addTitle(Sheet sheet, int rowNum, CellStyle titleStyle, String title) {
        Row row = sheet.createRow(rowNum++);
        row.setHeightInPoints(30); // 행 높이를 20 포인트로 설정
        createCell(row, 0, titleStyle, title);
        return rowNum;
    }

    private void createCell(Row row, int cellIndex, CellStyle cellStyle, String value) {
        Cell cell = row.createCell(cellIndex);
        cell.setCellValue(value);
        cell.setCellStyle(cellStyle);
    }

    public void settingInterfaceId(ApiInterfaceUI apiInterfaceUI, String elinkServiceId) {
        String interfaceId = elinkServiceId.substring(0, elinkServiceId.length() - 2); // 처음부터 끝에서 두 번째 문자 전까지 추출
        String sendRecvType = elinkServiceId.substring(elinkServiceId.length() - 2, elinkServiceId.length() - 1); // 끝에서 두 번째 글자
        int inOutType = Integer.parseInt(elinkServiceId.substring(elinkServiceId.length() - 1)); // 마지막 글자

        apiInterfaceUI.setInterfaceId(interfaceId);
        apiInterfaceUI.setSendRecvType(sendRecvType);
        apiInterfaceUI.setInOutType(inOutType);
    }
    
    /* 25.11.25 광주은행 엑셀 가져오기 기능 사용안함.
    public ApiInterfaceUI importInterfaceExcelSheet(Sheet sheet) throws Exception {
        String bizCode = PoiUtils.getCellValue(sheet, 1, 3);
        String elinkServiceId = PoiUtils.getCellValue(sheet, 1, 5);

        String interfaceDesc = PoiUtils.getCellValue(sheet, 2, 1);
        String transformYn = PoiUtils.getCellValue(sheet, 2, 3);
        String syncAsyncType = PoiUtils.getCellValue(sheet, 2, 5);

        String inboundAdapterGroupName = PoiUtils.getCellValue(sheet, 5, 1);
        String outboundAdapterGroupName = PoiUtils.getCellValue(sheet, 5, 3);
        String inboundResponseHttpMethod = "";
        String inboundResponseRestPath = "";
        String fromResponseAdapter = "";
        
        String outboundResponseHttpMethod = "";
        String outboundResponseRestPath = "";
        String toResponseAdapter = "";
        
        if (syncAsyncType.equals("asyncSync")) {
        	inboundResponseHttpMethod = PoiUtils.getCellValue(sheet, 8, 5);
        	inboundResponseRestPath = PoiUtils.getCellValue(sheet, 8, 7);
        	fromResponseAdapter = PoiUtils.getCellValue(sheet, 5, 5);
        } else if (syncAsyncType.equals("syncAsync")) {
        	outboundResponseHttpMethod = PoiUtils.getCellValue(sheet, 9, 5);
        	outboundResponseRestPath = PoiUtils.getCellValue(sheet, 9, 7);
        	toResponseAdapter = PoiUtils.getCellValue(sheet, 5, 5);
        }

        String inboundHttpMethod = PoiUtils.getCellValue(sheet, 8, 1);
        String inboundRestPath = PoiUtils.getCellValue(sheet, 8, 3);
        String outboundHttpMethod = PoiUtils.getCellValue(sheet, 9, 1);
        String outboundRestPath = PoiUtils.getCellValue(sheet, 9, 3);

        ApiInterfaceUI apiInterfaceUI = new ApiInterfaceUI();
        settingInterfaceId(apiInterfaceUI, elinkServiceId);

        apiInterfaceUI.setEaiBzwkDstcd(bizCode);
        apiInterfaceUI.setEaiSvcName(elinkServiceId);
        apiInterfaceUI.setEaiSvcDesc(interfaceDesc);
        apiInterfaceUI.setTransformYn(transformYn);
        apiInterfaceUI.setSyncAsyncType(syncAsyncType);

        apiInterfaceUI.setFromAdapter(inboundAdapterGroupName);
        apiInterfaceUI.setToAdapter(outboundAdapterGroupName);

        apiInterfaceUI.setInboundHttpMethod(inboundHttpMethod);
        apiInterfaceUI.setInboundRestPath(inboundRestPath);
        apiInterfaceUI.setOutboundHttpMethod(outboundHttpMethod);
        apiInterfaceUI.setOutboundRestPath(outboundRestPath);
        
        apiInterfaceUI.setInboundResponseHttpMethod(inboundResponseHttpMethod);
        apiInterfaceUI.setInboundResponseRestPath(inboundResponseRestPath);
        apiInterfaceUI.setFromResponseAdapter(fromResponseAdapter);

        apiInterfaceUI.setOutboundResponseHttpMethod(outboundResponseHttpMethod);
        apiInterfaceUI.setOutboundResponseRestPath(outboundResponseRestPath);
        apiInterfaceUI.setToResponseAdapter(toResponseAdapter);

        apiInterfaceUI.setIsHeaderRouting(false);

        if ("Y".equals(transformYn)) {
            apiInterfaceUI.setInboundRequestLayout(PoiUtils.getCellValue(sheet, 12, 1));
            apiInterfaceUI.setRequestTransform(PoiUtils.getCellValue(sheet, 12, 3));
            apiInterfaceUI.setOutboundRequestLayout(PoiUtils.getCellValue(sheet, 12, 5));
            apiInterfaceUI.setInboundResponseLayout(PoiUtils.getCellValue(sheet, 13, 1));
            apiInterfaceUI.setResponseTransform(PoiUtils.getCellValue(sheet, 13, 3));
            apiInterfaceUI.setOutboundResponseLayout(PoiUtils.getCellValue(sheet, 13, 5));
        }

        importApiInterfaceUi(apiInterfaceUI);

        return apiInterfaceUI;
    }
    */

    private void importApiInterfaceUi(ApiInterfaceUI apiInterfaceUI, boolean importDupliCheck) throws Exception {
        String elinkServiceId = apiInterfaceUI.getEaiSvcName();
        
        // 중복검사 실시
        boolean elinkServiceIdExists = eaiMessageQueryService.existsById(elinkServiceId);
        
        // 아이디가 중복 일 경우, 중복검사 체크여부에 따라 업데이트 진행.
        if (elinkServiceIdExists) {
        	// 중복검사 체크시 반환시킴
        	if (importDupliCheck) {
        		throw new IllegalStateException("ApiInterfaceId already exists [ " + elinkServiceId + " ]" );
        	} else {
        		update(apiInterfaceUI);
        	}
        } else {
        	insert(apiInterfaceUI);
        }
    }
    /* 25.11.25 광주은행 엑셀 가져오기 기능 사용안함.
    public ApiInterfaceReloadSyncSet importInterfaceFromExcel(MultipartFile file) throws Exception {
        if (file.isEmpty()) {
            throw new IOException("File is empty");
        }
        Workbook workbook;

        try (InputStream inputStream = file.getInputStream()) {
            workbook = new XSSFWorkbook(inputStream);
        }

        ApiInterfaceReloadSyncSet apiInterfaceReloadSyncSet = new ApiInterfaceReloadSyncSet();
        List<LayoutUI> layoutUIList = new ArrayList<>();
        apiInterfaceReloadSyncSet.setLayoutUIList(layoutUIList);
        List<TransformUI> transformUIList = new ArrayList<>();
        apiInterfaceReloadSyncSet.setTransformUIList(transformUIList);

        Iterator<Sheet> iterator = workbook.sheetIterator();
        while (iterator.hasNext()) {
            Sheet sheet = iterator.next();
            String type = PoiUtils.getCellValue(sheet, 1, 1);
            if (StringUtils.isBlank(type)) {
                type = PoiUtils.getCellValue(sheet, 1, 2);
            }
            switch (type) {
                case "INTERFACE":
                    ApiInterfaceUI apiInterfaceUI = importInterfaceExcelSheet(sheet);
                    apiInterfaceReloadSyncSet.setApiInterfaceUI(apiInterfaceUI);
                    break;
                case "LAYOUT":
                    layoutUIList.add(layoutManService.importLayoutExcelSheet(sheet));
                    break;
                case "TRANSFORM":
                    transformUIList.add(transform2Service.importTransformExcelSheet(sheet));
                    break;
            }
        }
        return apiInterfaceReloadSyncSet;
    }
    */

    public ApiInterfaceReloadSyncSet importInterfaceFromJson(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.registerModule(new JavaTimeModule());
            ApiInterfaceExportSetUI apiInterfaceExportSetUI = objectMapper.readValue(inputStream, ApiInterfaceExportSetUI.class);

            ApiInterfaceReloadSyncSet apiInterfaceReloadSyncSet = new ApiInterfaceReloadSyncSet();
            List<LayoutUI> layoutUIList = new ArrayList<>();
            apiInterfaceReloadSyncSet.setLayoutUIList(layoutUIList);

            importAndAddReloadSetLayout(layoutUIList, apiInterfaceExportSetUI.getInboundRequestLayout());
            importAndAddReloadSetLayout(layoutUIList, apiInterfaceExportSetUI.getOutboundRequestLayout());
            importAndAddReloadSetLayout(layoutUIList, apiInterfaceExportSetUI.getOutboundResponseLayout());
            importAndAddReloadSetLayout(layoutUIList, apiInterfaceExportSetUI.getInboundResponseLayout());

            List<TransformUI> transformUIList = new ArrayList<>();
            apiInterfaceReloadSyncSet.setTransformUIList(transformUIList);
            importAndAddReloadSetTransform(transformUIList, apiInterfaceExportSetUI.getRequestTransform());
            importAndAddReloadSetTransform(transformUIList, apiInterfaceExportSetUI.getResponseTransform());

            importApiInterfaceUi(apiInterfaceExportSetUI.getApiInterfaceUI(), importDupliCheck);
            apiInterfaceReloadSyncSet.setApiInterfaceUI(apiInterfaceExportSetUI.getApiInterfaceUI());

            return apiInterfaceReloadSyncSet;
        }
    }

    private void importAndAddReloadSetLayout(List<LayoutUI> layoutUIList, LayoutUI layoutUI) {
        if (layoutUI == null) {
            return;
        }
        layoutManService.importLayoutUi(layoutUI);
        layoutUIList.add(layoutUI);
    }

    private void importAndAddReloadSetTransform(List<TransformUI> transformUIList, TransformUI transformUI) {
        if (transformUI == null) {
            return;
        }
        transform2Service.importTransformUi(transformUI);
        transformUIList.add(transformUI);
    }

    public ApiInterfaceExportData exportInterfaceToJson(String elinkServiceId) throws Exception {
        ApiInterfaceUI apiInterfaceUI = selectDetail(elinkServiceId);
        settingInterfaceId(apiInterfaceUI, elinkServiceId);

        ApiInterfaceExportSetUI apiInterfaceExportSetUI = new ApiInterfaceExportSetUI();
        apiInterfaceExportSetUI.setApiInterfaceUI(apiInterfaceUI);

        if ("Y".equals(apiInterfaceUI.getTransformYn())) {
            if(StringUtils.isNotBlank(apiInterfaceUI.getInboundRequestLayout())) {
                apiInterfaceExportSetUI.setInboundRequestLayout(layoutManService.getLayoutUiToExport(apiInterfaceUI.getInboundRequestLayout()));
            }
            if(StringUtils.isNotBlank(apiInterfaceUI.getOutboundRequestLayout())) {
                apiInterfaceExportSetUI.setOutboundRequestLayout(layoutManService.getLayoutUiToExport(apiInterfaceUI.getOutboundRequestLayout()));
            }
            if(StringUtils.isNotBlank(apiInterfaceUI.getOutboundResponseLayout())) {
                apiInterfaceExportSetUI.setOutboundResponseLayout(layoutManService.getLayoutUiToExport(apiInterfaceUI.getOutboundResponseLayout()));
            }
            if(StringUtils.isNotBlank(apiInterfaceUI.getInboundResponseLayout())) {
                apiInterfaceExportSetUI.setInboundResponseLayout(layoutManService.getLayoutUiToExport(apiInterfaceUI.getInboundResponseLayout()));
            }
            if(StringUtils.isNotBlank(apiInterfaceUI.getRequestTransform())) {
                apiInterfaceExportSetUI.setRequestTransform(transform2Service.getTransformUiToExport(apiInterfaceUI.getRequestTransform()));
            }
            if(StringUtils.isNotBlank(apiInterfaceUI.getResponseTransform())) {
                apiInterfaceExportSetUI.setResponseTransform(transform2Service.getTransformUiToExport(apiInterfaceUI.getResponseTransform()));
            }
        }

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        ObjectWriter writer = objectMapper.writerWithDefaultPrettyPrinter();
        byte[] jsonBytes = writer.writeValueAsBytes(apiInterfaceExportSetUI);
        return new ApiInterfaceExportData(apiInterfaceUI, jsonBytes);
    }

    public String changeToTargetLayoutUI(LayoutUI layoutUI, String newBizCd, String newApiInterfaceId) {
        if (layoutUI == null)
            return null;
        layoutUI.setBzwkDsticName(newBizCd);
        String loutName = layoutUI.getLoutName();

        String replacementBeforeUnderscore = newBizCd;
        String replacementBetweenUnderscores = newApiInterfaceId;

        // 정규 표현식 패턴: 첫 번째 언더바 전의 단어와 두 번째 언더바 사이의 단어를 찾기 위한 패턴
        // (서비스ID 등에 '-'가 포함될 수 있으므로 \w+ 대신 [^_]+ 사용 - transform 치환 로직과 동일)
        String regex = "(^[^_]+)|(?<=_)[^_]+(?=_)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(loutName);

        // 첫 번째 매칭된 부분(언더바 전의 단어)과 두 번째 매칭된 부분(언더바 사이의 단어)을 대체
        String updatedString = matcher.replaceFirst(replacementBeforeUnderscore);  // 첫 번째 매칭된 부분 대체
        updatedString = updatedString.replaceFirst("(?<=_)[^_]+(?=_)", replacementBetweenUnderscores);  // 두 번째 매칭된 부분 대체

        layoutUI.setLoutName(updatedString);

        for (LayoutItemUI layoutItemUI : layoutUI.getLayoutItems()) {
            layoutItemUI.setLoutName(updatedString);
        }

        layoutManService.importLayoutUi(layoutUI);

        return updatedString;
    }

    public String changeToTargetTransformUI(TransformUI transformUI, String newBizCd, String newApiInterfaceId, String srcLayoutName, String tgtLayoutName) {
        if (transformUI == null)
            return null;
        transformUI.setEaiBzwkDstcd(newBizCd);

        String replacementSecondWord = newBizCd;
        String replacementThirdWord = newApiInterfaceId;

        String cnvsnName = transformUI.getCnvsnName();

        // 정규 표현식 패턴: 언더바로 구분된 단어들을 캡처하는 패턴
        String regex = "([^_]+)_([^_]+)_([^_]+)_([^_]+)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(cnvsnName);

        // 매칭된 부분을 새로운 단어로 대체
        String updatedString = matcher.replaceAll("$1_" + replacementSecondWord + "_" + replacementThirdWord + "_$4");

        transformUI.setCnvsnName(updatedString);

        transformUI.setSrcLayoutName(srcLayoutName);
        transformUI.setTgtLayoutName(tgtLayoutName);

        for (TransformItemUI transformItemUI : transformUI.getGridData()) {
            transformItemUI.setCnvsnName(updatedString);

            String tgtPath = transformItemUI.getCnvsnRsultItemPathName().replaceFirst("^[^.]+", tgtLayoutName);
            transformItemUI.setCnvsnRsultItemPathName(tgtPath);
            String srcPath = transformItemUI.getCnvsncmdname().replaceFirst("^[^.]+", srcLayoutName);
            transformItemUI.setCnvsncmdname(srcPath);
        }

        transform2Service.importTransformUi(transformUI);

        return updatedString;
    }

    public ApiInterfaceUI cloneApiInterface(ApiInterfaceUI orgApiInterfaceUI, String newBizCd, 
    		String newApiInterfaceId, String newInboundHttpMethod, 
    		String newInboundRestPath, String newSvcDesc, boolean importDupliCheck) throws Exception {
    	
        Optional<EAIMessageEntity> optional = eaiMessageQueryService.findById(newApiInterfaceId);
        if (optional.isPresent()) {
            String bzwkDstcd = optional.get().getEaibzwkdstcd();
            throw new BizException("[" + bzwkDstcd + "][" + newApiInterfaceId + "]로 이미 등록된 인터페이스입니다.\n조회가 되지 않을 시 담당자에게 문의바랍니다.");
        }
        
        importDupliCheck = false; // 중복검사 완료.

        ApiInterfaceUI apiInterfaceUI = orgApiInterfaceUI;
        apiInterfaceUI.setEaiBzwkDstcd(newBizCd);
        settingInterfaceId(apiInterfaceUI, newApiInterfaceId);
        apiInterfaceUI.setEaiSvcName(newApiInterfaceId);
        apiInterfaceUI.setEaiSvcDesc(newSvcDesc);
        apiInterfaceUI.setInboundHttpMethod(newInboundHttpMethod);
        apiInterfaceUI.setInboundRestPath(newInboundRestPath);

        if ("Y".equals(apiInterfaceUI.getTransformYn())) {
        	
        	String newInReq  = processLayoutIfExist(apiInterfaceUI.getInboundRequestLayout(),  newBizCd, newApiInterfaceId);
        	String newOutReq = processLayoutIfExist(apiInterfaceUI.getOutboundRequestLayout(), newBizCd, newApiInterfaceId);
        	String newOutRes = processLayoutIfExist(apiInterfaceUI.getOutboundResponseLayout(), newBizCd, newApiInterfaceId);
        	String newInRes  = processLayoutIfExist(apiInterfaceUI.getInboundResponseLayout(),  newBizCd, newApiInterfaceId);
        	
        	apiInterfaceUI.setInboundRequestLayout(newInReq);
            apiInterfaceUI.setOutboundRequestLayout(newOutReq);
            apiInterfaceUI.setOutboundResponseLayout(newOutRes);
            apiInterfaceUI.setInboundResponseLayout(newInRes);
            
            if (newInReq != null && newOutReq != null) {
	            apiInterfaceUI.setRequestTransform(
	            		changeToTargetTransformUI(
	            				transform2Service.getTransformUiToExport(apiInterfaceUI.getRequestTransform()), 
	            				newBizCd, newApiInterfaceId, newInReq, newOutReq
	            		)
	            );
            }
            
            if (newOutRes != null && newInRes != null) {
	            apiInterfaceUI.setResponseTransform(
	            		changeToTargetTransformUI(
	            				transform2Service.getTransformUiToExport(apiInterfaceUI.getResponseTransform()), 
	            				newBizCd, newApiInterfaceId, newOutRes, newInRes
	            		)
	            );
            }
            
        }

        importApiInterfaceUi(apiInterfaceUI, importDupliCheck);

        return apiInterfaceUI;
    }
    
    /*
     * 복제시 레이아웃 정보가 있는 경우에만 변환 로직을 수행
     */
    private String processLayoutIfExist(String layoutId, String newBizCd, String newApiInterfaceId) {
    	
    	if (layoutId == null || layoutId.isEmpty()) {
    		return null;
    	}
    	
    	LayoutUI layoutEntity  = layoutManService.getLayoutUiToExport(layoutId);
    	
		return changeToTargetLayoutUI(layoutEntity, newBizCd, newApiInterfaceId);
    	
    }
}
