package itn.com.cmm.util;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;

import itn.com.cmm.MjonMsgSendVO;
import itn.let.mail.service.StatusResponse;
import itn.let.mjo.msg.service.MjonMsgVO;
import itn.let.mjo.spammsg.web.ComGetSpamStringParser;
import itn.let.module.base.PriceAndPoint;
import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @author 		: 이호영
 * @fileName 	: MsgSendUtils.java 
 * @date 		: 2024.09.23
 * @description : 메세지발송 데이터 다루는 유틸
 * =========================================================== 
 * DATE          AUTHOR   NOTE 
 * ----------------------------------------------------------- *
 * 2024.09.23    이호영          최초 생성
 * 
 * 
 * 
 */
@Slf4j
public final class MsgSendUtils {

	
	/** 
	 * @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(StringUtils.isNotEmpty(smsTxt)) {
			String smsCont = smsTxt.replace("\r\n", "\n");
			smsBytes = smsCont.getBytes(charset).length;
		}
		log.info(" + smsBytes :: [{}]", smsBytes);
		return smsBytes;
	}

	/** 
	 * @methodName	: getMsgType 
	 * @author		: 이호영
	 * @date		: 2024.09.23 
	 * @description	: msgType 재정의
	 * @param mjonMsgVO
	 * @param smsTxtByte
	 * @return 
	 * @throws UnsupportedEncodingException 
	 */
	public static String getMsgTypeWithByteValidation(MjonMsgSendVO sendVO, String p_smsTxt) throws UnsupportedEncodingException {
		
		
		int smsTxtByte = getSmsTxtBytes(p_smsTxt);
		String msgType = "4";
	
	//	// 내문자저장함에 저장 후 문자를 발송하는 경우 문자 타입이 숫자가 아닌 문자로 넘어와서 변경 처리함
	//	if ("P".equals(msgType) || "L".equals(msgType)) {
	//	    msgType = "6";
	//	} else if ("S".equals(msgType)) {
	//	    msgType = "4";
	//	}
	
		// 그림 이미지가 첨부된 경우 장문으로 설정
		if (StringUtils.isNotEmpty(sendVO.getFilePath1())) {
			msgType = "6";
		} else if (smsTxtByte > 2000) {
			// 2000 Byte를 초과할 경우 에러 처리 (이 부분은 호출부에서 검사하도록 유지할 수도 있음)
			return "INVALID";  // 이 값은 호출부에서 에러 처리를 하도록 활용할 수 있습니다.
		} else if (smsTxtByte > 90) {
			// 90Byte 초과 시 장문으로 설정
			msgType = "6";
		}

		return msgType;
	}

	public static float getValidPrice(Float personalPrice, Float defaultPrice) {
	    return (personalPrice != null && personalPrice > 0) ? personalPrice : defaultPrice;
	}

	
	/** 
	* @methodName	: determinePriceByMsgType 
	* @author		: 이호영
	* @date		: 2024.09.24 
	* @description	: 문자 메시지 타입에 따라 단가를 결정하는 메서드
	* @param mjonMsgVO
	* @param shortPrice
	* @param longPrice
	* @param picturePrice
	* @param picture2Price
	* @param picture3Price
	* @return 
	*/
	public static float determinePriceByMsgType(MjonMsgVO mjonMsgVO, float shortPrice, float longPrice, 
									float picturePrice, float picture2Price, float picture3Price) {
		float price = 0;
		String msgType = mjonMsgVO.getMsgType();
	
		if ("4".equals(msgType)) {
			price = shortPrice;
		} else if ("6".equals(msgType)) {
			// 파일 첨부 여부에 따라 장문 단가 설정
			if (mjonMsgVO.getFileName3() != null) {
				price = picture3Price;
			} else if (mjonMsgVO.getFileName2() != null) {
				price = picture2Price;
			} else if (mjonMsgVO.getFileName1() != null) {
				price = picturePrice;
			} else {
				price = longPrice;
			}
		}
		return price;
	}

	
	/** 
	 * @methodName	: isReplacementRequired 
	 * @author		: 이호영
	 * @date		: 2024.11.12 
	 * @description	: 치환데이터가 있는지 확인
	 * @param mjonMsgVO
	 * @return 
	 */
	public static boolean isRepleasYN(MjonMsgVO mjonMsgVO) {
		
		// 치환 구문 패턴 리스트
		String[] placeholders = {"\\[\\*이름\\*\\]", "\\[\\*1\\*\\]", "\\[\\*2\\*\\]", "\\[\\*4\\*\\]", "\\[\\*3\\*\\]"};
		
		for (String placeholder : placeholders) {
			Pattern pattern = Pattern.compile(placeholder);
			Matcher matcher = pattern.matcher(mjonMsgVO.getSmsTxt());
			// 해당 패턴이 존재하면 true 반환
			if (matcher.find()) {
				return true;
			}
		}
		return false;
	
	}


	/** 
	 * @methodName	: populateReplacementLists 
	 * @author		: 이호영
	 * @date		: 2024.09.26 
	 * @description	: 배열에 데이터를 채우는 메서드 
	 * @param mjonMsgVO
	 * @param lists
	 * @param statusResponse
	 * @return
	 * @throws Exception 
	 */
	public static Boolean populateSendLists(MjonMsgVO mjonMsgVO, List<MjonMsgSendVO> mjonMsgSendListVO, StatusResponse statusResponse, List<String> resultSpamTxt) throws Exception{
		
		log.info(" :: populateSendLists :: ");

		int spamChkSize = getSpamChkSize(mjonMsgSendListVO.size());
		int sampleCounter = 0;

		String smsTxtTemp = mjonMsgVO.getSmsTxt();

		// 치환 구문과 필드 getter 매핑
		Map<String, Function<MjonMsgSendVO, String>> placeholders = new HashMap<>();
		placeholders.put("[*이름*]", MjonMsgSendVO::getName);
		placeholders.put("[*1*]", MjonMsgSendVO::getRep1);
		placeholders.put("[*2*]", MjonMsgSendVO::getRep2);
		placeholders.put("[*3*]", MjonMsgSendVO::getRep3);
		placeholders.put("[*4*]", MjonMsgSendVO::getRep4);

		boolean hasPerformedSpamCheck = false; // 치환 문자가 없는 경우, 스팸 체크가 한 번만 수행되도록 제어
		boolean hasPerformedMsgType = false; // 치환 문자가 없는 경우, 스팸 체크가 한 번만 수행되도록 제어
		Boolean replaceYN = getReplaceYN(smsTxtTemp);
		
		String msgTypeResult = null;
		
		for (MjonMsgSendVO sendVO : mjonMsgSendListVO) {

			String smsTxt = smsTxtTemp;
			// 치환 문자면
			if(replaceYN) {
				
				// 각 치환 구문을 확인하고 치환할 값이 없으면 오류 반환
				for (Map.Entry<String, Function<MjonMsgSendVO, String>> entry : placeholders.entrySet()) {
					String placeholder = entry.getKey();
					String value = entry.getValue().apply(sendVO);
					if (smsTxtTemp.contains(placeholder)) {
						if (StringUtils.isEmpty(value)) {
							statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "치환 문구중 " + placeholder + " 데이터가 없습니다.");
							return false;
						}
						smsTxt = smsTxtTemp.replace(placeholder, value);
					}
				}
			}

			String smsSpamChkTxt = mjonMsgVO.getSmsTxt().replaceAll(String.valueOf((char) 13), "");
			// 스팸문자 체크
			// 치환문자가 아닐 경우
			if (!replaceYN && !hasPerformedSpamCheck) {
				checkSpamAndSetStatus(mjonMsgVO, smsSpamChkTxt, resultSpamTxt);
				hasPerformedSpamCheck = true; // 스팸 체크가 한 번만 수행되도록 설정
			// 치환 문자가 있는 경우에는 spamChkSize 카운트까지만 수행
			} else if (replaceYN && sampleCounter < spamChkSize && !"Y".equals(mjonMsgVO.getSpamStatus())) {
				checkSpamAndSetStatus(mjonMsgVO, smsSpamChkTxt, resultSpamTxt);
				sampleCounter++;
			}
			log.info(" ++ smsTxt:: [{}]", smsTxt);
			sendVO.setSmsTxt(smsTxt);
			
			// 이미지 셋팅
			setImagePathsForMsgSendVO(mjonMsgVO, sendVO);
			

			// msgType 셋팅 및 문자열 체크
			log.info(" + smsTxt :: [{}]", smsTxt);
			if (!replaceYN && !hasPerformedMsgType) {
				log.info(" 치환 X ");
				// byte 체크와 msgType 구하기
				msgTypeResult = getMsgTypeWithByteValidation(sendVO, smsTxt);
				if ("INVALID".equals(msgTypeResult)) {
					statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.");
					return false;
				}
				hasPerformedMsgType = true; // 스팸 체크가 한 번만 수행되도록 설정
			}else if(replaceYN){
				log.info(" 치환 O ");
				// byte 체크와 msgType 구하기
				msgTypeResult = getMsgTypeWithByteValidation(sendVO, smsTxt);
				if ("INVALID".equals(msgTypeResult)) {
					statusResponseSet(statusResponse, HttpStatus.BAD_REQUEST, "문자 치환 후 전송 문자 길이를 초과하였습니다.");
					return false;
				}
			}

			sendVO.setMsgType(msgTypeResult);
			
		}
		
		return true;

	}
	
	private static void setImagePathsForMsgSendVO(MjonMsgVO mjonMsgVO, MjonMsgSendVO sendVO) {
		int fileCount = Integer.parseInt(mjonMsgVO.getFileCnt());

		switch (fileCount) {
			case 3:
				sendVO.setFilePath3(mjonMsgVO.getFileName3());
			case 2:
				sendVO.setFilePath2(mjonMsgVO.getFileName2());
			case 1:
				sendVO.setFilePath1(mjonMsgVO.getFileName1());
				break;
			default:
				// fileCount가 0이거나 설정할 파일이 없는 경우
				break;
		}
		sendVO.setFileCnt(mjonMsgVO.getFileCnt());
	}

	private static void checkSpamAndSetStatus(MjonMsgVO mjonMsgVO, String personalizedSmsTxt, List<String> resultSpamTxt) throws Exception {
		String resultParser = ComGetSpamStringParser.getSpamTextParse(personalizedSmsTxt).trim();
		int spmCnt = 0;

		for (String spmTxt : resultSpamTxt) {
			String parserStr = ComGetSpamStringParser.getSpamTextParse(spmTxt).trim();
			if (resultParser.contains(parserStr)) {
				spmCnt++;
			}
		}

		if (spmCnt > 0) { // 스팸 문자가 포함된 경우
			System.out.println("++++++++++++++ spam smsTxt ::: " + resultParser);
			mjonMsgVO.setSpamStatus("Y");
		}else {mjonMsgVO.setSpamStatus("N");}
	}

	private static Boolean getReplaceYN(String smsTxtTemplate) {// 여러 치환 구문이 포함된 정규식 패턴
		
		Boolean replaceYN = false;
		
		Pattern pattern = Pattern.compile("\\[\\*이름\\*\\]|\\[\\*1\\*\\]|\\[\\*2\\*\\]|\\[\\*3\\*\\]|\\[\\*4\\*\\]");
		Matcher matcher = pattern.matcher(smsTxtTemplate);

		// 정규식 패턴에 해당하는 치환 구문이 존재하는지 확인
		if (matcher.find()) {
			replaceYN = true;
		}
		return replaceYN;
	}

	/** 
	 * @methodName	: getSpamChkSize 
	 * @author		: 이호영
	 * @date		: 2024.11.13 
	 * @description	: 수신자 건수별로 스팸체크하는 갯수 설정 
	 * @param size
	 * @return 
	 */
	private static int getSpamChkSize(int size) {
		int chkSize = 1; // 기본 샘플 크기

		// 수신자 수에 따른 샘플 크기 결정
		if (size > 100 && size <= 1000) {
			chkSize = 10;
		} else if (size > 1000) {
			chkSize = 50;
		}
		
		return chkSize;
	}

	/**
	 * 특정 플레이스홀더를 리스트에서 가져온 값으로 치환합니다.
	 * 
	 * @param smsTxt		문자 내용
	 * @param placeholder	치환할 플레이스홀더 (예: [*이름*])
	 * @param list			치환할 데이터 리스트
	 * @param index			현재 인덱스
	 * @return 치환된 문자 내용
	 */
	private static String replacePlaceholder(String smsTxt, String placeholder, String[] list, int index) {
		if (smsTxt.contains(placeholder)) {
			if (list.length > index && StringUtil.isNotEmpty(list[index])) {
				smsTxt = smsTxt.replaceAll(placeholder, StringUtil.getString(list[index].replaceAll("§", ",")));
			} else {
				smsTxt = smsTxt.replaceAll(placeholder, "");
			}
		}
		return smsTxt;
	}

	// 배열 인덱스를 안전하게 접근하는 메서드
	private static String getSafeValue(String[] array, int index) {
	    return (array != null && array.length > index) ? array[index] : " ";
	}

	// 치환 처리에 특수문자 제거 로직이 포함된 메서드
	private static String getReplacementValue(String[] array, int index, String placeholder) {
	    if (array != null && array.length > index && StringUtil.isNotEmpty(array[index])) {
	        return StringUtil.getString(array[index].replaceAll("§", ","));
	    } else {
	        return " ";
	    }
	}
	
	

	private static void statusResponseSet (StatusResponse statusResponse, HttpStatus httpStatus, String msg ) {
		statusResponse.setStatus(httpStatus);
		statusResponse.setMessage(msg);
		
	}

	public static StatusResponse validateFilesForMessageSending(int fileCount, MjonMsgVO mjonMsgVO) {
		if (fileCount > 0) {
			boolean allFilesAreNull = Stream
					.of(mjonMsgVO.getFileName1(), mjonMsgVO.getFileName2(), mjonMsgVO.getFileName3())
					.allMatch(Objects::isNull);

			if (allFilesAreNull) {
				return new StatusResponse(HttpStatus.NO_CONTENT, "문자 메세지 이미지 추가에 오류가 발생하여 문자 발송이 취소 되었습니다.");
			}
		}
		return null; // 유효성 검사를 통과한 경우
	}


	/** 
	 * @methodName	: validateReplacementData 
	 * @author		: 이호영
	 * @date		: 2024.09.25
	 * @description	: 치환문자가 사용될 때 데이터가 올바른지 확인하는 메서드
	 * @param mjonMsgVO
	 * @param list
	 * @return boolean
	 */
	/*
	 * public static boolean validateReplacementData(MjonMsgVO mjonMsgVO,
	 * List<MjonMsgSendVO> list) { // 치환 구문과 필드 getter 매핑 Map<String,
	 * Function<MjonMsgSendVO, String>> placeholders = new HashMap<>();
	 * placeholders.put("\\[\\*이름\\*\\]", MjonMsgSendVO::getName);
	 * placeholders.put("\\[\\*1\\*\\]", MjonMsgSendVO::getRep1);
	 * placeholders.put("\\[\\*2\\*\\]", MjonMsgSendVO::getRep2);
	 * placeholders.put("\\[\\*3\\*\\]", MjonMsgSendVO::getRep3);
	 * placeholders.put("\\[\\*4\\*\\]", MjonMsgSendVO::getRep4);
	 * 
	 * // smsTxt 에서 필요한 치환 구문이 포함되어 있는지 확인 String smsTxt = mjonMsgVO.getSmsTxt();
	 * for (Map.Entry<String, Function<MjonMsgSendVO, String>> entry :
	 * placeholders.entrySet()) { if
	 * (Pattern.compile(entry.getKey()).matcher(smsTxt).find()) { // 해당 치환 구문이 존재할
	 * 경우 모든 수신자에서 필드 값 확인 for (MjonMsgSendVO recipient : list) { if
	 * (StringUtils.isEmpty(entry.getValue().apply(recipient))) { return false; //
	 * 데이터가 없는 경우 } } } } // 모든 치환 구문이 유효한 데이터를 가지고 있으면 true 반환 return true; }
	 */
	
	// 배열이 비어있는지 확인하는 메서드
	public static boolean isEmpty(String[] array) {
		return array == null || array.length == 0;
	}

	
}
