package itn.let.mjo.mjocommon;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import egovframework.com.idgen.CustomIdGnrService;
import egovframework.rte.fdl.cmmn.exception.FdlException;
import itn.com.cmm.MjonMsgSendVO;
import itn.com.cmm.OptimalMsgResultDTO;
import itn.com.cmm.util.MsgSendUtils;
import itn.com.cmm.util.SlackMessageFormatUtil;
import itn.com.cmm.util.StringUtil;
import itn.let.kakao.kakaoComm.KakaoSendAdvcVO;
import itn.let.kakao.kakaoComm.KakaoVO;
import itn.let.mail.service.StatusResponse;
import itn.let.mjo.event.service.MjonEventService;
import itn.let.mjo.event.service.MjonEventVO;
import itn.let.mjo.msg.service.MjonMsgVO;
import itn.let.mjo.msg.service.impl.MjonMsgDAO;
import itn.let.mjo.msgdata.service.MjonMsgDataService;
import itn.let.mjo.msgdata.service.MjonMsgReturnVO;
import itn.let.mjo.msgholiday.service.MsgAlarmSetVO;
import itn.let.mjo.msgholiday.service.MsgHolidayService;
import itn.let.mjo.msgholiday.service.MsgHolidayVO;
import itn.let.sym.site.service.EgovSiteManagerService;
import itn.let.sym.site.service.JoinSettingVO;
import itn.let.uat.uia.web.SendLogVO;
import itn.let.uss.umt.service.EgovUserManageService;
import itn.let.uss.umt.service.UserManageVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service("MjonCommon")
public class MjonCommon {
	

	/** userManageService */
	@Resource(name = "userManageService")
	private EgovUserManageService userManageService;

	/** 사이트 설정 */ 
	@Resource(name = "egovSiteManagerService")
	EgovSiteManagerService egovSiteManagerService;	

	@Resource(name = "MjonEventService")
	private MjonEventService mjonEventService;
	
	@Resource(name = "MsgHolidayService")
	private MsgHolidayService msgHolidayService;

	@Value("#{globalSettings['Globals.slack.hooks.url']}")
	private String SLACK_URL;
	
	/** xpedite 솔루션 ID*/
	@Value("#{globalSettings['Globals.slack.channel.name']}")
	private String SLACK_CHANNEL;

	@Resource(name = "egovMjonMsgIdCGnrService")
	private CustomIdGnrService idgenMsgCId;
	
	@Resource(name = "mjonMsgDAO")
	private MjonMsgDAO mjonMsgDAO;
	
	@Resource(name = "MjonMsgDataService")
	private MjonMsgDataService mjonMsgDataService;

	

	/** 
	 * @methodName	: getAdminSandSlack 
	 * @author		: 이호영
	 * @date		: 2024.12.04 
	 * @description	: 기존 메소드 리펙토링
	 * @param mjonMsgVO 
	 */
	public void getAdminSandSlack(String smsTxt, String sandName) {
	
		HttpClient client = new HttpClient();
		PostMethod post = new PostMethod(SLACK_URL);
	
		try {
			// 메시지 내용 설정
		
			// Slack 메시지 생성
			JSONObject json = new JSONObject();
			json.put("channel", SLACK_CHANNEL);
			json.put("text", smsTxt);
			json.put("username", sandName);
		
			// Slack 요청
			post.addParameter("payload", json.toString());
			post.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		
			// Slack 응답 처리
			int responseCode = client.executeMethod(post);
			if (responseCode != HttpStatus.SC_OK) {
				log.warn("Slack 메시지 전송 실패. Response: {}", post.getResponseBodyAsString());
			}
	
		} catch (IllegalArgumentException e) {
			log.error("Slack 메시지 전송 중 IllegalArgumentException 발생", e);
		} catch (IOException e) {
			log.error("Slack 메시지 전송 중 IOException 발생", e);
		} catch (Exception e) {
			log.error("Slack 메시지 전송 중 Exception 발생", e);
		} finally {
			post.releaseConnection();
		}
	}
	

	/** 
	 * @methodName	: getAdminMsgSandSlack 
	 * @author		: 이호영
	 * @date		: 2024.12.04 
	 * @description	: 기존 메소드 리펙토링
	 * @param mjonMsgVO 
	 */
	public void getAdminMsgSandSlack(MjonMsgVO mjonMsgVO) {
	
		HttpClient client = new HttpClient();
		PostMethod post = new PostMethod(SLACK_URL);
	
		try {
			// 메시지 내용 설정
			String smsTxt = SlackMessageFormatUtil.formatSmsText(mjonMsgVO);
			String sandName = SlackMessageFormatUtil.formatSandName(mjonMsgVO);
		
			// Slack 메시지 생성
			JSONObject json = new JSONObject();
			json.put("channel", SLACK_CHANNEL);
			json.put("text", smsTxt);
			json.put("username", sandName);
		
			// Slack 요청
			post.addParameter("payload", json.toString());
			post.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		
			// Slack 응답 처리
			int responseCode = client.executeMethod(post);
			if (responseCode != HttpStatus.SC_OK) {
				log.warn("Slack 메시지 전송 실패. Response: {}", post.getResponseBodyAsString());
			}
	
		} catch (IllegalArgumentException e) {
			log.error("Slack 메시지 전송 중 IllegalArgumentException 발생", e);
		} catch (IOException e) {
			log.error("Slack 메시지 전송 중 IOException 발생", e);
		} catch (Exception e) {
			log.error("Slack 메시지 전송 중 Exception 발생", e);
		} finally {
			post.releaseConnection();
		}
	}
	
	public void getAdminKakaoAtSendSlack(KakaoSendAdvcVO kakaoVO) {
		HttpClient client = new HttpClient();
		PostMethod post = new PostMethod(SLACK_URL);

		try {
			// 메시지 내용 설정
			String smsTxt = SlackMessageFormatUtil.formatKakaoText(kakaoVO);
			String sandName = SlackMessageFormatUtil.formatKakaoSandName(kakaoVO);
			
			// Slack 메시지 생성
			JSONObject json = new JSONObject();
			json.put("channel", SLACK_CHANNEL);
			json.put("text", smsTxt);
			json.put("username", sandName);
			
			// Slack 요청
			post.addParameter("payload", json.toString());
			post.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		
			// Slack 응답 처리
			int responseCode = client.executeMethod(post);
			if (responseCode != HttpStatus.SC_OK) {
				log.warn("Slack 메시지 전송 실패. Response: {}", post.getResponseBodyAsString());
			}
		} catch (IllegalArgumentException e) {
			log.error("Slack 메시지 전송 중 IllegalArgumentException 발생", e);
		} catch (IOException e) {
			log.error("Slack 메시지 전송 중 IOException 발생", e);
		} catch (Exception e) {
			log.error("Slack 메시지 전송 중 Exception 발생", e);
		} finally {
			post.releaseConnection();
		}
	}
	
	private String formatKakaoSandName(KakaoVO kakaoVO) {
		// TODO Auto-generated method stub
		return null;
	}

	


	/**
	* @Method Name : sendSimpleSlackMsg
	* @작성일 : 2022. 12. 9
	* @작성자 :  AnJooYoung
	* @Method 설명 : slack 단순 메시지 전송
	*/
	@SuppressWarnings("unchecked")
	public void sendSimpleSlackMsg(String msg) {
		
		HttpClient client = new HttpClient();
		PostMethod post = new PostMethod(SLACK_URL);
		JSONObject json = new JSONObject();
		try {
			
			json.put("channel", SLACK_CHANNEL);
			//json.put("channel", "C04DNV4FYP6");	//개발 서버용
			
			json.put("text", msg);
			
			post.addParameter("payload", json.toString());
			// 처음에 utf-8로 content-type안넣어주니까 한글은 깨져서  content-type넣어줌
			post.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			int responseCode = client.executeMethod(post);
			String response = post.getResponseBodyAsString();
			if (responseCode != HttpStatus.SC_OK) { 
				System.out.println("Response: " + response);
			}
		} catch (IllegalArgumentException e) {
			System.out.println("IllegalArgumentException posting to Slack " + e);
		} catch (IOException e) {
			System.out.println("IOException posting to Slack " + e);
		} catch (Exception e) {
			System.out.println("Exception posting to Slack " + e);
		} finally {
			post.releaseConnection();
		}
		
	}
	
	
	/**
	 * 관리자로 문자 발송해주기
	 * 사용자가 보낸 문자를 문자온 법인폰으로 발송해주는 기능 함수.
	 * 일반문자 와 대량 문자 모두 적용하고 있음
	 * 2022.09.19 우영두 추가
	 * 2023.01.26 우영두 수정 => 대표전송사로 발송되도록 수정
	 * */
	public MjonMsgVO getAdminPhoneSendMsgDataComm(MjonMsgVO mjonMsgVO) throws Exception{
		
		try {
			mjonMsgVO.setUserId("system");//시스템 발송 문자로 처리
			
			//전송사 선택
			String msgType = mjonMsgVO.getMsgType();
			int fileCount = Integer.parseInt(mjonMsgVO.getFileCnt());//그림 이미지 갯수
			
			if(msgType.equals("6")) {//장문 혹은 그림문자인 경우
				if(fileCount > 0) {//그림문자인 경우
					mjonMsgVO.setNeoType("4");
				}				
			}
			
			//수신번호가 배열로 되어있어서 배열에 담아준다.
			String[] phone = new String[1];
			String callTo = "15518011";
			phone[0] = callTo;
			mjonMsgVO.setCallToList(phone);//수신번호 리스트
			
			//시스템 로그용 수신 정보
			mjonMsgVO.setCallTo("help@iten.co.kr");
			
			//현재 고객의 보유 캐시가 문자 발송이 가능한 금액인지 체크
	    	//String userMoney = "0.0";
	    	String userPoint = "0.0";
	    	mjonMsgVO.setBefPoint(userPoint); //현재 보유 포인트 정보 저장
	    	mjonMsgVO.setBefCash("0.0");		//관리자가 발송하는 것이라서 0원으로 입력
	    	mjonMsgVO.setMsgGroupCnt("1");
	    	
	    	//문자종류 관리자가 발송하는 것은 msgKind : S 로 셋팅
			mjonMsgVO.setMsgKind("S");
			
			Date now = new Date();
			SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
			mjonMsgVO.setReqDate(sdFormat.format(now));
			
			//예약 문자 발송 없이 즉시 발송으로 처리
			mjonMsgVO.setReserveYn("N");
			
		} catch (Exception e) {
			System.out.println("+++++++++++++++++++++++++++++ MjonCommon Class getAdminPhoneSendMsgData Function Error !!!" + e);
		}
		
		return mjonMsgVO;
	}
	
	
	/**
	 * 관리자가 사용자에게 문자 발송해주기
	 * 발신번호 승인 / 반려시 사용자에게 문자 발송해 주기.
	 * 일반문자 와 대량 문자 모두 적용하고 있음
	 * 2022.09.26 우영두 추가
	 * 
	 * */
	public MjonMsgVO getAdminToMberPhoneSendMsgDataComm(MjonMsgVO mjonMsgVO) throws Exception{
		
		try {
			mjonMsgVO.setUserId("system");//시스템 발송 문자로 처리
			
			//전송사 선택
			String msgType = mjonMsgVO.getMsgType();
			int fileCount = Integer.parseInt(mjonMsgVO.getFileCnt());//그림 이미지 갯수
			
			if(msgType.equals("6")) {//장문 혹은 그림문자인 경우
				if(fileCount > 0) {//그림문자인 경우
					mjonMsgVO.setNeoType("4");
				}
			}
			
			//수신번호가 배열로 되어있어서 배열에 담아준다.
			String[] phone = new String[1];
			String callTo = mjonMsgVO.getCallTo();
			phone[0] = callTo;
			mjonMsgVO.setCallToList(phone);//수신번호 리스트
			
			//시스템 로그용 수신 정보
			mjonMsgVO.setCallTo(callTo);
			
			//현재 고객의 보유 캐시가 문자 발송이 가능한 금액인지 체크
	    	//String userMoney = "0.0";
	    	String userPoint = "0.0";
	    	mjonMsgVO.setBefPoint(userPoint); //현재 보유 포인트 정보 저장
	    	mjonMsgVO.setBefCash("0.0");		//관리자가 발송하는 것이라서 0원으로 입력
	    	mjonMsgVO.setMsgGroupCnt("1");
	    	
	    	//문자종류 관리자가 발송하는 것은 msgKind : S 로 셋팅
			mjonMsgVO.setMsgKind("S");
			
			//예약 문자 발송 없이 즉시 발송으로 처리
			mjonMsgVO.setReserveYn("N");
			
		} catch (Exception e) {
			System.out.println("+++++++++++++++++++++++++++++ MjonCommon Class getAdminToMberPhoneSendMsgData Function Error !!!" + e);
		}
		
		return mjonMsgVO;
	}
	/*
	@SuppressWarnings("unchecked")
	public void getAdminKakaoAtSandSlack(KakaoVO kakaoVO) {
		
		HttpClient client = new HttpClient();
		PostMethod post = new PostMethod(SLACK_URL);
		JSONObject json = new JSONObject();
		try {
			
			String reserveYn = kakaoVO.getReserveYn();
			String atDelayYn = kakaoVO.getAtDelayYn();
			String smsTxt = kakaoVO.getTemplateContent();
			String reservSmsTxt = "";
			String smisingSmsTxt = "";
			//예약문자를 발송하는 경우 문자 내용 앞에 "[예약]" 표시되도록 처리
			if(reserveYn.equals("Y")) {
				
				if(atDelayYn.equals("Y")) {//예약문자 중 스미싱의심 일 경우
					reservSmsTxt = "[스미싱의심][예약]" + smsTxt;
				}else {
					reservSmsTxt = "[예약]" + smsTxt;
				}
				smsTxt = reservSmsTxt;
			}else if(atDelayYn.equals("Y")) {
				
				smisingSmsTxt = "[스미싱의심]" + smsTxt;
				smsTxt = smisingSmsTxt;
			}
			
			String sandName = kakaoVO.getCallFrom();
			String userId = kakaoVO.getUserId();
			String msgType = "";
			
			if(kakaoVO.getMsgType().equals("8")) {
				msgType = "[알림톡]";
			}else if(kakaoVO.getMsgType().equals("9")){
				msgType = "[친구톡]";
			}
			sandName = "[" + userId + "]" + "[" + sandName + "]" + msgType;
			
			json.put("channel", SLACK_CHANNEL);
			json.put("text", smsTxt);
			json.put("username", sandName);
			
			
			post.addParameter("payload", json.toString());
			// 처음에 utf-8로 content-type안넣어주니까 한글은 깨져서  content-type넣어줌
			post.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			int responseCode = client.executeMethod(post);
			String response = post.getResponseBodyAsString();
			if (responseCode != HttpStatus.SC_OK) { 
				System.out.println("Response: " + response);
			}
		} catch (IllegalArgumentException e) {
			System.out.println("IllegalArgumentException posting to Slack " + e);
		} catch (IOException e) {
			System.out.println("IOException posting to Slack " + e);
		} catch (Exception e) {
			System.out.println("Exception posting to Slack " + e);
		} finally {
			post.releaseConnection();
		}
		
	}
	*/
	
	public String getCreateMsgUserIdgen(String subUserId, String lastId) throws Exception{
		
		String returnId = "";
		String[] splitId = lastId.split("_");
		
		if(splitId.length > 0) {
			
			int lastNum = Integer.parseInt(splitId[1]);
			
			lastNum = lastNum + 1;
			
			String tmpLastNum = Integer.toString(lastNum);
			
			int zeroPlusCnt = 14 - tmpLastNum.length();
			
			StringBuilder sb = new StringBuilder();
			sb.append(subUserId + "_");
			
			for(int i=0; i< zeroPlusCnt; i++) {
				
				sb.append('0');
				
			}
			
			sb.append(tmpLastNum);

			returnId = sb.toString();
			
		}
		
		return returnId;
	}




private String formatSandName(MjonMsgVO mjonMsgVO) {
    String userId = mjonMsgVO.getUserId();
    String callFrom = mjonMsgVO.getCallFrom();
    String msgType = getMessageTypeLabel(mjonMsgVO);

    return String.format("[%s][%s]%s", userId, callFrom, msgType);
}

private String getMessageTypeLabel(MjonMsgVO mjonMsgVO) {
    String msgType = mjonMsgVO.getMsgType();
    int fileCount = parseIntOrDefault(mjonMsgVO.getFileCnt(), 0);

    switch (msgType) {
        case "4":
            return "[단문]";
        case "6":
            return fileCount == 0 ? "[장문]" : "[그림]";
        default:
            return "";
    }
}


private int parseIntOrDefault(String value, int defaultValue) {
    try {
        return Integer.parseInt(value);
    } catch (NumberFormatException e) {
        return defaultValue;
    }
}
	
	
	
	

	/**
	 * @methodName	: processUserAndCheckSms 
	 * @author		: 이호영
	 * @date		: 2025. 3. 25.
	 * @description	: SMS 알림 전체 로직 처리 (한 번에 모든 필요한 정보 반환)
	 * @return : boolean
	 * @param mjonMsgVO
	 * @param userId
	 * @return
	 * @throws Exception
	 * 
	 */
	public boolean processUserAndCheckSms(MjonMsgVO mjonMsgVO, String userId) throws Exception {
		UserManageVO userManageVO = getUserManageInfo(userId);

		// 기본값 처리된 사용자 정보와 문자 상태
		String adminSmsNoticeYn = userManageVO.getAdminSmsNoticeYn();
		String smishingYn = userManageVO.getSmishingYn();
		String spamStatus = safeGetString(mjonMsgVO.getSpamStatus());

		// 조건 체크
		if ("Y".equals(adminSmsNoticeYn) || "Y".equals(spamStatus) || "Y".equals(smishingYn)) {
			mjonMsgVO.setSmishingYn(smishingYn); // MjonMsgVO에 스미싱 정보 설정

			// 스미싱 알림 처리
			return handleSmishingAlert(); // 알림 처리 결과 반환
		}

		return false; // 알림 처리되지 않음
	}
	
	/**
	 * @methodName	: processUserAndCheckAT 
	 * @author		: 이호영
	 * @date		: 2025. 3. 25.
	 * @description	: SMS 알림 전체 로직 처리 (한 번에 모든 필요한 정보 반환)
	 * @return : boolean
	 * @param mjonMsgVO
	 * @param userId
	 * @return
	 * @throws Exception
	 * 
	 */
	public boolean processUserAndCheckAT(KakaoVO kakaoVO) throws Exception {
		UserManageVO userManageVO = getUserManageInfo(kakaoVO.getUserId());
		
		// 기본값 처리된 사용자 정보와 문자 상태
		String adminSmsNoticeYn = userManageVO.getAdminSmsNoticeYn(); // 법인폰 알람 여부 - Y : ON 
		String atSmishingYn = userManageVO.getAtSmishingYn(); // 스미싱 의심 - Y : ON
		
		// 조건 체크
		if ("Y".equals(adminSmsNoticeYn) || "Y".equals(atSmishingYn)) {
			kakaoVO.setAtSmishingYn("Y"); // MjonMsgVO에 스미싱 정보 설정 - Y면 디
			
			// 스미싱 알림 처리
			return handleSmishingAlert(); // 알림 처리 결과 반환
		}
		
		return false; // 알림 처리되지 않음
	}
	
	
	/**
	 * @methodName	: processUserAndCheckFT 
	 * @author		: 이호영
	 * @date		: 2025. 8. 21.
	 * @description	: 
	 * @return : boolean
	 * @param kakaoVO
	 * @return
	 * @throws Exception
	 * 
	 * @isHolidayNotified 
	 * @false : 알림 X
	 * @true  : 알림 O 
	 * 
	 */
	public boolean processUserAndCheckFT(KakaoVO kakaoVO) throws Exception {
//		UserManageVO userManageVO = getUserManageInfo(kakaoVO.getUserId());
//		kakaoVO.setAtSmishingYn("N"); // MjonMsgVO에 스미싱 정보 설정 - Y면 딜레이 처리 됨
		
		
		// 기본값 처리된 사용자 정보와 문자 상태
//		String adminSmsNoticeYn = userManageVO.getAdminSmsNoticeYn(); // 법인폰 알람 여부 - Y : ON 
//		String atSmishingYn = userManageVO.getAtSmishingYn(); // 스미싱 의심 - Y : ON !== mj_msg_group_data와 다른거임
		
		// 조건 체크
//		if ("Y".equals(adminSmsNoticeYn) || "Y".equals(atSmishingYn)) {
//		if ("Y".equals(atSmishingYn)) {
//			Boolean B_return = handleSmishingAlert();
//			if(B_return) { // true면 알림ON이라서 스미싱Yn을 Y로 설정 아니면 N / 나머지는 로직에서 처리 
//				kakaoVO.setAtSmishingYn(atSmishingYn); // MjonMsgVO에 스미싱 정보 설정 - Y면 딜레이 처리 됨
//			}
			// 스미싱 알림 처리
//			return B_return; // 알림 처리 결과 반환
//		}
		
		return handleSmishingAlert(); // 알림 처리되지 않음
	}
	

	// 사용자 정보 조회 및 기본값 처리
	public UserManageVO getUserManageInfo(String userId) throws Exception {
//		UserManageVO userManageVO = new UserManageVO();
//		userManageVO.setAdminSmsNoticeYn("Y"); // 기본값
//		userManageVO.setSmishingYn("N"); // 기본값
//		
		return userManageService.selectAdminSmsNoticeYn(new UserManageVO(userId));
	}

	// 스미싱 알림 처리
	public boolean handleSmishingAlert() throws Exception {
		/**
		 * MJ_MBER_SETTING => 기본 시스템 알림 여부
		 * 슬랙 Y
		 * 야간스미싱알림 Y
		 * 등등
		 *
		 */
		JoinSettingVO joinSettingVO = egovSiteManagerService.selectAdminNotiDetail();
		
/** @시스템 설정에 야간스미싱 알림 || 슬랙알림이 N이면 false*/
		if (joinSettingVO == null || !"Y".equals(joinSettingVO.getHoliSmishingNoti()) || 
			!"Y".equals(joinSettingVO.getSlackNoti())) {
			return false; // 알림 조건 미충족
		}

/** @MJ_SPAMPASS_ALARM : 현재 활성화된 알림 SELECT */ 
		List<MsgAlarmSetVO> alarmList = getAlarmSettings();
/** @MJ_HOLIDAY 시스템에 등록된 공휴일 설정 */
		List<MsgHolidayVO> holidayList = getHolidayList();
/** @MJ_HOLIDAY 시스템에 등록된 공휴일 설정 */
		boolean isNotificationAllowed = new MjonHolidayApi().getHolidaySmishingPassStatus_advc(alarmList, holidayList);

		return !isNotificationAllowed; // 알림 발송 조건 미충족
	}

	// 안전하게 문자열 가져오기
	private String safeGetString(String value) {
		return value == null ? "" : value;
	}

	// 알림 설정 조회
	private List<MsgAlarmSetVO> getAlarmSettings() throws Exception {
		MsgAlarmSetVO msgAlarmSetVO = new MsgAlarmSetVO();
		msgAlarmSetVO.setUseYn("Y");
		msgAlarmSetVO.setFirstIndex(0);
		msgAlarmSetVO.setRecordCountPerPage(10000);
		return msgHolidayService.selectAlarmSettingList(msgAlarmSetVO);
	}

	// 공휴일 정보 조회
	private List<MsgHolidayVO> getHolidayList() throws Exception {
		Calendar calendar = Calendar.getInstance();
		int year = calendar.get(Calendar.YEAR);

		MsgHolidayVO msgHolidayVO = new MsgHolidayVO();
		msgHolidayVO.setFirstIndex(0);
		msgHolidayVO.setRecordCountPerPage(10000);
		msgHolidayVO.setSearchHoliYear(Integer.toString(year));
		return msgHolidayService.selectMsgHolidayList(msgHolidayVO);
	}

	// 이벤트 메시지 처리
	public StatusResponse processEventMessages(String userId, MjonMsgVO mjonMsgVO, 
											List<MjonMsgSendVO> mjonMsgSendVOList, MjonEventVO eventMberInfo) throws Exception {
		
		StatusResponse statusResponse = new StatusResponse();

		try {
			// 이벤트 여부 확인
			if (MsgSendUtils.isNotEvent(eventMberInfo)) {
				return statusResponse; // 이벤트 상태가 종료이거나 endDate가 없는 경우 처리하지 않음
			}
			log.info(" + 이벤트 진행 대상자 :: [{}]", userId);
			log.info(" + 이벤트 진행 대상자 eventMberInfo.getMberId() :: [{}]", eventMberInfo.getMberId());

			// 최적화된 메시지 리스트 및 이벤트 정보 가져오기
			OptimalMsgResultDTO result = MsgSendUtils.getOptimalMsgList(eventMberInfo, mjonMsgSendVOList);
			List<MjonMsgSendVO> optimalMsgList = result.getOptimalMsgList();
			MjonEventVO returnEventMberInfo = result.getEventInfo();

			// 이벤트 발송 내역이 있으면
			if (CollectionUtils.isNotEmpty(optimalMsgList)) {
				mjonMsgVO.setEventYn("Y"); // 그룹에 이벤트 발송 여부 설정
				mjonMsgSendVOList.addAll(optimalMsgList); // 기존 리스트와 병합
			}

			// 이벤트 관련 데이터가 있으면 updqte
			eventChkAndInsertAmount(returnEventMberInfo);

		} catch (IllegalArgumentException e) {
			
			// 메시지 타입 에러 처리
			MsgSendUtils.statusResponseSet(statusResponse, org.springframework.http.HttpStatus.BAD_REQUEST, "이벤트 데이터 처리 중 오류가 발생하였습니다.\n관리자에게 문의해 주세요.");
			
		}

		return statusResponse;
	}


	/** 
	 * @methodName	: checkEventAndDeductAmount 
	 * @author		: 이호영
	 * @date		: 2025.02.10 
	 * @description	: 
	 * @param eventMberInfo
	 * @param eachPrice 
	 * @throws Exception 
	 */
	public void checkEventAndDeductAmount(MjonEventVO eventMberInfo, List<MjonMsgSendVO> groupedMsgList) throws Exception {
		
		// 이벤트 그룹인지 확인
		if("Y".equals(groupedMsgList.get(0).getEventYn())) {
			// 이벤트 남은 금액
			double remainCash = eventMberInfo.getEventRemainCash();
			// 현재 그룹에서 발송한 총 금액
			float sendTotalPrice = MsgSendUtils.setPriceforVO(groupedMsgList);
			
			// 이벤트 남은 금액에서 현재 그룹에 총 발송금액 차감
			remainCash -= sendTotalPrice;
			
			MjonEventVO returnEventMberInfo = returnEventMberInfo = MsgSendUtils.terminateEvent(eventMberInfo, remainCash);
			
			eventChkAndInsertAmount(returnEventMberInfo);		
		} 
		
	}
	

	/** 
	 * @methodName	: eventChkAndInsertAmount 
	 * @author		: 이호영
	 * @date		: 2025.02.10 
	 * @description	: 이벤트 관련 데이터가 있으면 update
	 * @param returnEventMberInfo
	 * @throws Exception 
	 */
	private void eventChkAndInsertAmount(MjonEventVO returnEventMberInfo) throws Exception {
		// 이벤트 상태 종료 시 업데이트
		if (returnEventMberInfo != null) {
			mjonEventService.updateEventEndStatus(returnEventMberInfo);
		}
	}
	


	public List<String> getNextCustomMsgCId (int cnt) throws FdlException {

		List<String> idList = idgenMsgCId.getNextStringId(cnt);
		return idList;
		
	}
	
	
	/** 
	 * @methodName	: getSmsTxtBytes 
	 * @author		: 이호영
	 * @date		: 2024.09.23 
	 * @description	: sms 텍스트 바이트 계산 후 return;
	 * @param smsTxt
	 * @return
	 * @throws UnsupportedEncodingException 
	 */
	public static int getSmsTxtBytes(String smsTxt) throws UnsupportedEncodingException {        	//문자열 길이 체크 해주기
		int smsBytes = 0;
		//문자 바이트 계산에 필요한 캐릭터 셋 : 한글 2Byte로 계산
		String charset = "euc-kr"; 						
		if(org.apache.commons.lang3.StringUtils.isNotEmpty(smsTxt)) {
			String smsCont = smsTxt.replace("\r\n", "\n");
			smsBytes = smsCont.getBytes(charset).length;
		}
//		log.info(" + smsBytes :: [{}]", smsBytes);
		return smsBytes;
	}


	/**
	 * @methodName	: replaceTemplateVariables 
	 * @author		: 이호영
	 * @date		: 2025. 3. 12.
	 * @description	: 헬퍼 메서드: 템플릿 변수 치환
	 * @return : String
	 * @param content
	 * @param variables
	 * @return
	 */
	public static String ATReplaceTemplateVariables(String content, Map<String, String> variables) {
		String result = content;
		for (Map.Entry<String, String> entry : variables.entrySet()) {
			String placeholder = entry.getKey();
			String value = entry.getValue();
			result = result.replace(placeholder, value);
		}
		return result;
	}
	
	public void sendMessagesIfOverFifty(int cnt, String callTo) throws Exception {
		if(
				cnt >= 50 											//50건 이상일 경우만 발송
				&& StringUtil.isNotEmpty(callTo)					// null, "" 체크
				&& callTo.startsWith("010")							// 010으로 시작하는 휴대폰번호일 경우
				&& "Y".equals(selectSmsNotiIfOverFiftySetting())	// 안내문자 발송 on 인경우만
				&& !sysMsgTodaySendYn(callTo)						// 금일 1회 이상 보낸 이력없을 경우만 발송
			) {
			
			//50건이상 발송 안내문자 구분값 = 01
			String sendMsgType = "01";
			String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
			String contents = "[인터넷 대량문자발송안내] 문자온에서 "+ callTo + " 번호로 " + today + "에 인터넷 대량 문자(50통 이상)가 발송되었습니다."
					+ "\n※ 번호도용이 의심되는 경우, 이용 중인 이동통신사의 휴대전화 번호도용 문자차단 부가서비스(무료)에 가입하여 피해를 예방할 수 있습니다.";
			this.sendSysMsg(
					"15518011"
					, callTo
					, contents
					, sendMsgType
					);
		}
	}
	
	/**
	 * Method Name : sendSysMsg
	 * Description : 관리자 안내 문자 공통
	 *
	 * @param callFrom : 발신번호
	 * @param callTo : 수신번호 
	 * @param contents : 내용 
	 * @param sendMsgType : 발신내용 타입 - 01: 대량문자발송안내
	 * @return
	 * @throws Exception
	 * @return MjonMsgReturnVO 설명
	 */
	@Transactional(rollbackFor = Exception.class)
	public MjonMsgReturnVO sendSysMsg(
										String callFrom
										, String callTo
										, String contents
										, String sendMsgType
									) throws Exception {
		
		//결과 vo
		MjonMsgReturnVO returnVO = new MjonMsgReturnVO();
		
		MjonMsgVO mjonMsgVO = new MjonMsgVO();
		mjonMsgVO.setSmsTxt(contents);
		mjonMsgVO.setReserveYn("N");
		// 시스템 문자발송 번호
//		mjonMsgVO.setCallFrom("15518011");
		mjonMsgVO.setCallFrom(callFrom);
		mjonMsgVO.setCallTo(callTo);

		mjonMsgVO.setUserId("system");

		/*
		 * 본문길이에 따른 단문/장문 구분
		 * 단문 4
		 * 장문 6
		 * 2000자 이상 invalid
		 */
		String msgType = MsgSendUtils.getMsgTypeWithByteValidation(new MjonMsgSendVO(), contents);// 
		mjonMsgVO.setMsgType(msgType);
		
		// MsgDiv - S: 단문, L: 장문, P: 그림
		if("4".equalsIgnoreCase(msgType)) {
			mjonMsgVO.setMsgDiv("S");
		}else if("6".equalsIgnoreCase(msgType)) {
			mjonMsgVO.setMsgDiv("L");
		}
		else { // invalid
			returnVO.setMsgGroupId("");
			returnVO.setSendMsgCnt("0"); // 발송 건수 저장
			returnVO.setSendMsgBlockCnt("0"); // 수신차단 건수 저장
			return returnVO;
		}
			
		// 문자타입별 대표전송사 정보
		MjonMsgVO mjonMsgVO2 = new MjonMsgVO();
		mjonMsgVO2 = mjonMsgDAO.selectRepMsgAgetnInfo(mjonMsgVO);
		// 전송사 구분 코드 - 01 : 아이하트, 02 : 현대 퓨쳐넷, 03 : 아이엠오, 04 : 다우기술
    	mjonMsgVO.setAgentCode(mjonMsgVO2.getAgentCode()); //전송사 선택			
    	// 전송금액
		mjonMsgVO.setTotPrice(mjonMsgVO2.getAgentPrice().toString());	//총금액
		mjonMsgVO.setEachPrice(mjonMsgVO2.getAgentPrice().toString());	//한건 금액

		returnVO = mjonMsgDataService.insertSysMsgDataInfo(mjonMsgVO);
		
		// 시스템 발송 로그
		SendLogVO sendLogVO = new SendLogVO();
		// SendType 1:문자로 발송 2:이메일로 발송
		sendLogVO.setSendId(returnVO.getMsgGroupId());
		sendLogVO.setSendType("1");
		sendLogVO.setFrstSendInfo(mjonMsgVO.getCallFrom());
		sendLogVO.setReceive(mjonMsgVO.getCallTo());
		sendLogVO.setContents(contents);
		sendLogVO.setSendMsgType(sendMsgType);

		mjonMsgDataService.insertSysMsgLog(sendLogVO);
		
		return returnVO;
	}
	
	private Boolean sysMsgTodaySendYn(String callTo) throws Exception {
		SendLogVO sendLogVO = new SendLogVO();
		sendLogVO.setReceive(callTo);
		
		return mjonMsgDataService.selectSysMsgTodaySendYn(sendLogVO);
	}
	
	private String selectSmsNotiIfOverFiftySetting() throws Exception {
		JoinSettingVO joinSettingVO = egovSiteManagerService.selectAdminNotiDetail();
		return joinSettingVO.getSmsNotiIfOverFifty();
	}
	
}
