package itn.let.schdlr.service;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;

import egovframework.rte.fdl.property.EgovPropertyService;
import itn.com.uss.olh.hpc.service.HackIpService;
import itn.com.utl.fcc.service.EgovDateUtil;
import itn.let.fax.admin.service.FaxAdmService;
import itn.let.fax.admin.service.FaxStatVO;
import itn.let.kakao.admin.kakaoAt.service.MjonKakaoAtStatVO;
import itn.let.kakao.admin.statistics.service.KakaoStatisticsService;
import itn.let.kakao.kakaoComm.KakaoVO;
import itn.let.kakao.user.kakaoAt.service.KakaoAlimTalkService;
import itn.let.lett.service.LetterService;
import itn.let.mail.service.MailTemplateService;
import itn.let.mjo.mjocommon.MjonCommon;
import itn.let.mjo.msg.service.MjonMsgService;
import itn.let.mjo.msg.service.MjonMsgStatVO;
import itn.let.mjo.msgdata.service.impl.MjonMsgDataDAO;
import itn.let.mjo.pay.service.MjonPayService;
import itn.let.mjo.pay.service.MjonPayVO;
import itn.let.sts.com.StatsVO;
import itn.let.sts.cst.service.EgovConectStatsService;
import itn.let.uss.umt.service.EgovUserManageService;
import itn.let.utl.user.service.MjonNoticeSendUtil;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.core.SchedulerLock;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;

@Profile({"!local"})
@Service("SchedulerUtil")
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S") // Scheduler Lock 사용 가능 설정 (기본 30초동안 Lock)
public class SchedulerUtil {
	
	@Resource(name = "SchdlrManageService")
    private SchdlrManageService schdlrManageService;
	
	@Resource (name = "MailTemplateService")
	private MailTemplateService mailTemplateService;
	
	@Resource (name = "userManageService")
	private EgovUserManageService egovUserManageService;	
	
	@Resource(name = "hackIpService")
	private HackIpService hackIpService;
	
	@Resource(name = "mjonMsgService")
    private MjonMsgService mjonMsgService;
	
	@Resource(name = "KakaoStatisticsService")
    private KakaoStatisticsService kakaoStatisticsService;
	
    @Resource(name = "propertiesService")
    protected EgovPropertyService propertyService;

	@Resource(name = "conectStatsService")
    private EgovConectStatsService conectStatsService;

	/** 알림전송 Util */
	@Resource(name = "mjonNoticeSendUtil")
	private MjonNoticeSendUtil mjonNoticeSendUtil;
	
	@Resource(name = "faxAdmService")
	private FaxAdmService faxAdmService;
	
	@Resource(name = "LetterService")
    private LetterService letterService;
	
	@Resource(name="MjonMsgDataDAO")
	private MjonMsgDataDAO mjonMsgDataDAO;
	
	@Resource(name="kakaoAlimTalkService")
	private KakaoAlimTalkService kakaoAlimTalkService;
	
	@Resource(name="MjonCommon")
	private MjonCommon mjonCommon;

	@Resource(name = "mjonPayService")
    private MjonPayService mjonPayService;
	
    /** 설정값 가져오기 */
	@Value("#{globalSettings['Globals.Env']}")
	private String GlobalsEnv;
	
	private static final String ONE_MIN = "PT1M"; // 1분동안 LOCK
	
	/*
	 * 10분 마다 1주일 데이터에서 환불 대상을 찾아서 환불 한다.
	 * */
	// Schdule Lock (10분동안)	
	@Scheduled(cron = "0 0/10 * * * ?")
	@SchedulerLock(name = "runScenarioOneTime", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runScenarioOneTime() throws Exception {
		
		// do something...
		try {
			System.out.println("=============SchedulerUtil=====runScenarioOneTime =============>");
			System.out.println("=============SchedulerUtil=====runScenarioOneTime =============>");
			System.out.println("=============SchedulerUtil=====runScenarioOneTime =============>");
			System.out.println("=============SchedulerUtil=====runScenarioOneTime =============>");
			System.out.println("=============SchedulerUtil=====runScenarioOneTime =============>");
			
			//문자 환불, 팩스 환불
			
			
			PayBack("");
			
			/*
			// 문자 환불
			schdlrManageService.msgFailPayBack();	
			 * */				
			
				
			// do something...
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}
	
	/*
	 * 매일 5시20분 20초에 31일전 데이터에서 환불 대상을 찾아서 환불 한다.
	 * */
	@Scheduled(cron = "20 25 5 * * ?")
	@SchedulerLock(name = "runScenarioOneTimeOneByDay", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runScenarioOneTimeOneByDay() throws Exception {
		
		// do something...
		try {
			System.out.println("=============EgovSysLogScheduling=====runScenarioOneTimeOneByDay =============>");
			//문자 환불, 팩스 환불
			PayBack("ONE");
			
			/*
			// 문자 환불
			schdlrManageService.msgFailPayBack();	
			 * */
			
				
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}
	
	// Schdule Lock (2분동안)
	//세틀뱅크 자동 충전 배치
	@Scheduled(cron = "0 0/2 * * * ?")
	@SchedulerLock(name = "runVacsChargeOneTime", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runVacsChargeOneTime() throws Exception {

		System.out.println("=============EgovSysLogScheduling=====runVacsChargeOneTime =============>");
		// do something...
		try {
				
			schdlrManageService.vacsAutoCharge();				
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}

	// 매일 오전 4시마다 실행 ex) 04:00
	// 휴면회원으로 지정
	@Scheduled(cron = "0 0 4 * * *")
	@SchedulerLock(name = "runMemberDormantUpdate", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runMemberDormantUpdate() throws Exception {

		System.out.println("=============EgovSysLogScheduling=====runMemberDormantUpdate =============>");
		try {
				
			mailTemplateService.setMemberDormantUpdate();				
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}		
	
	// 매일 오전 8시마다 실행 ex) 08:00
	// 휴면회원 메일발송
	@Scheduled(cron = "0 0 8 * * *")
	@SchedulerLock(name = "runMailSendMemberDormant", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runMailSendMemberDormant() throws Exception {

		System.out.println("=============EgovSysLogScheduling=====runMailSendMemberDormant =============>");
		try {
				
			mailTemplateService.mailSendMemberDormant();				
				
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}	

	// 매일 오전 10시마다 실행 ex) 10:00
	// 휴면회원 SMS발송
	@Scheduled(cron = "0 0 10 * * *")
	@SchedulerLock(name = "runSmsSendMemberDormant", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runSmsSendMemberDormant() throws Exception {

		System.out.println("=============EgovSysLogScheduling=====runSmsSendMemberDormant =============>");
		try {
				
			mailTemplateService.smsSendMemberDormant();				
				
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}	
	
	// 매일 오전 5시마다 실행 ex) 05:00
	// 문자 우선순위 랜덤 업데이트 All
	@Scheduled(cron = "0 0 5 * * *")
	@SchedulerLock(name = "runLetterPriorityUpdateAll", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runLetterPriorityUpdateAll() throws Exception {

		System.out.println("=============EgovSysLogScheduling=====runLetterPriorityUpdateAll =============>");
		try {
				
			letterService.updateLetterPriorityAll();				
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}	
	
	// 매달 1일 0시 10분 실행
	// "0 10 0 1 * *"
	// 매달 3일 6시 40분 실행
	// "0 40 18 3 * *"
	// 매일 오전 1시 1분 0초
	// "0  1  1  *  *  *"
//	@Scheduled(cron = "0 * * * * *")	// 1분마다 실행(TEST)
	@Scheduled(cron = "0 1 0 1 * *")	// 매달 1일 0시 1분 실행
	@SchedulerLock(name = "runUserCashByAutoCashUpdate", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runUserCashByAutoCashUpdate() throws Exception {
		//System.out.println("AutoCash Start");
		
		// 후불제회원 Cash충전
		//egovUserManageService.updateUserCashByAutoCash();
		
		//System.out.println("AutoCash End");
		
		try {
				
				System.out.println("AutoCash Start");
				
				// 후불제회원 Cash충전
				egovUserManageService.updateUserCashByAutoCash();
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}	
	
	
	// 트래픽 감시 스케줄러
	// 3분마다 실행
	@Scheduled(cron = "0 0/3 * * * ?") 	
	@SchedulerLock(name = "highTrafficIgnoreIpInsert", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void highTrafficIgnoreIpInsert() throws Exception {
		
		//5분 내 300회 이상 접속로그가 있는 경우 차단아이피 등록
		//hackIpService.selectHighTrafficNotIgnoreIp();
		
		try {
			System.out.println("=============EgovSysLogScheduling=====highTrafficIgnoreIpInsert =============>");
				
				
			//5분 내 300회 이상 접속로그가 있는 경우 차단아이피 등록
			hackIpService.selectHighTrafficNotIgnoreIp();				
				
				
			// do something...
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
		
	}
	
	// AGENT별 SMS LMS MMS 발송건수 , 카카오 알림톡 발송건수 통계 스케쥴러
	@Scheduled(cron = "0 10 1 * * *")		// 매일 새벽1시 10분 실행
	@SchedulerLock(name = "insertAgentSmsCountStat", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void agentSmsCountStatInsert() throws Exception {
		try {
			System.out.println("=============EgovSysLogScheduling=====insertAgentSmsCountStat scheduler RUN (YESTERDAY) =============>");
			int minusDate =EgovDateUtil.dayMinusToInt("yyyyMMdd", 1);
			int minusSevenDays =EgovDateUtil.dayMinusToInt("yyyyMMdd", 7);

			// 전일  문자발송 건 통계 insert
			MjonMsgStatVO mjonMsgStatVO = new MjonMsgStatVO();
			mjonMsgStatVO.setStatStartDate(minusSevenDays);
			mjonMsgStatVO.setStatEndDate(minusDate);
			mjonMsgService.insertAgentSmsCountStat(mjonMsgStatVO);

			// 전일 알림톡발송 건 통계 insert
			MjonKakaoAtStatVO kakaoAtStatVO = new MjonKakaoAtStatVO();
			kakaoAtStatVO.setStatStartDate(minusSevenDays);
			kakaoAtStatVO.setStatEndDate(minusDate);
			kakaoStatisticsService.insertKakaoAtCountStat(kakaoAtStatVO);

			// 전일 알림톡발송 건 통계 insert
			FaxStatVO faxStatVO = new FaxStatVO();
			faxStatVO.setStatStartDate(minusSevenDays);
			faxStatVO.setStatEndDate(minusDate);
			faxAdmService.insertFaxCountStat(faxStatVO);
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}
	
	// AGENT별 SMS 발송건수 통계 스케쥴러
	@Scheduled(cron = "0 5,35 * * * *") 	// 5,35분마다 실행
	@SchedulerLock(name = "insertAgentSmsCountStatByMinute", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void agentSmsCountStatByMinuteInsert() throws Exception {
		try {
			System.out.println("=============EgovSysLogScheduling=====insertAgentSmsCountStatByMinute =============>");
				
			int nowDate =EgovDateUtil.nowDateToInt("yyyyMMdd");

			// 당일 문자발송 건 통계 insert
			MjonMsgStatVO mjonMsgStatVO = new MjonMsgStatVO();
			mjonMsgStatVO.setStatStartDate(nowDate);
			mjonMsgStatVO.setStatEndDate(nowDate);
			mjonMsgService.insertAgentSmsCountStat(mjonMsgStatVO);
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	// AGENT별 Kakao 발송건수 통계 스케쥴러
	//@Scheduled(cron = "0 10,40 * * * *") 	// 10,40분마다 실행
	//@Scheduled(cron = "0 47 * * * *") 	// 47분마다 실행
	@Scheduled(cron = "0 57 23 * * *")
	@SchedulerLock(name = "insertAgentKakaoCountStatByMinute", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void agentKakaoCountStatByMinuteInsert() throws Exception {
		try {
			System.out.println("=============EgovSysLogScheduling=====insertAgentKakaoCountStatByMinute =============>");
				
			int nowDate =EgovDateUtil.nowDateToInt("yyyyMMdd");
			
			// 당일 알림톡발송 건 통계 insert
			MjonKakaoAtStatVO kakaoAtStatVO = new MjonKakaoAtStatVO();
			kakaoAtStatVO.setStatStartDate(nowDate);
			kakaoAtStatVO.setStatEndDate(nowDate);
			kakaoStatisticsService.insertKakaoAtCountStat(kakaoAtStatVO);
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	// AGENT별 Fax 발송건수 통계 스케쥴러
	@Scheduled(cron = "0 15,45 * * * *") 	// 15,45분마다 실행
	@SchedulerLock(name = "insertAgentFaxCountStatByMinute", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void agentFaxCountStatByMinuteInsert() throws Exception {
		try {
			System.out.println("=============EgovSysLogScheduling=====insertAgentFaxCountStatByMinute =============>");
				
			int nowDate =EgovDateUtil.nowDateToInt("yyyyMMdd");

			// 전일 카톡발송 건 통계 insert
			FaxStatVO faxStatVO = new FaxStatVO();
			faxStatVO.setStatStartDate(nowDate);
			faxStatVO.setStatEndDate(nowDate);
			faxAdmService.insertFaxCountStat(faxStatVO);
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	// 대시보드 어제/오늘 접속현황 & 접속통계 
	@Scheduled(cron = "0 1 0 * * *")		// 매일 새벽0시 1분 실행
	@SchedulerLock(name = "insertDashBoardStat", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void dashBoardStatInsert() throws Exception {
		try {
			System.out.println("=============EgovSysLogScheduling=====dashBoardStatInsert =============>");
				// 어제날짜
				Calendar calendar = new GregorianCalendar();
				SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
				
				String yesterdayDate = SDF.format(calendar.getTime());		
				calendar.add(Calendar.DATE, -1);		
				yesterdayDate = SDF.format(calendar.getTime());		

				StatsVO yesterdayStatsVO = new StatsVO();
				yesterdayStatsVO.setStatDate(yesterdayDate);
				
				// 대시보드 인서트 Logic
				conectStatsService.insertDashBoardStatLogic(yesterdayStatsVO);
		
				// 대시보드 업데이트 Logic
				conectStatsService.updateDashBoardStatLogic(yesterdayStatsVO);				
				
				
				// 오늘날짜
				Date nowDate = new Date();
				SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); 
				String strNowDate = simpleDateFormat.format(nowDate); 
				
				StatsVO statsVO = new StatsVO();
				statsVO.setStatDate(strNowDate);

				// 대시보드 인서트 Logic
				conectStatsService.insertDashBoardStatLogic(statsVO);
		
				// 대시보드 업데이트 Logic
				conectStatsService.updateDashBoardStatLogic(statsVO);
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
	}
	
	// 대시보드 어제/오늘 접속현황 & 접속통계 
	@Scheduled(cron = "0 20,50 * * * *") 	// 20,50분마다 실행
	@SchedulerLock(name = "updateDashBoardStat", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void dashBoardStatUpdate() throws Exception {
		
		try {
			System.out.println("=============EgovSysLogScheduling=====DashBoardStatUpdate =============>");
			// 오늘날짜
			Date nowDate = new Date();
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); 
			String strNowDate = simpleDateFormat.format(nowDate); 
			
			StatsVO statsVO = new StatsVO();
			statsVO.setStatDate(strNowDate);

			// 대시보드 인서트 Logic
			conectStatsService.insertDashBoardStatLogic(statsVO);
	
			// 대시보드 업데이트 Logic
			conectStatsService.updateDashBoardStatLogic(statsVO);
			
			
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}

	// 이용약관 메일발송
	@Scheduled(cron = "0 */15 * * * *") 	// 15분마다 실행
	@SchedulerLock(name = "sendTermsEmail", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void TermsEmailSend() throws Exception {
		
		try {
			System.out.println("=============EgovSysLogScheduling=====TermsEmailSend =============>");
				
			// 이용약관 회원 전체발송
			mjonNoticeSendUtil.userAllTermsEmailSend();
		}catch(Exception ex) {
			ex.printStackTrace();
		}
	}
	
	@Bean
	public LockProvider lockProvider(DataSource dataSource) {
		return new JdbcTemplateLockProvider(dataSource);
	}

//	@Scheduled(cron = "0 0 0/2 * * ?") // 매시간 짝수 정각마다 실행
	@Scheduled(cron = "0 0/3 * * * ?") 	// 3분마다 실행
	@SchedulerLock(name = "runKakaoOneTime", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runKakaoOneTime() throws Exception {
		
			/* 회원 money 업데이트 처리 트랜잭션 분리를 위하여 impl이 아닌 현재 위치에서 반복문 실행 */
			System.out.println("=============SchedulerUtil=====runKakaoOneTime =============>");
			List<KakaoVO> kakaoRefundList = kakaoAlimTalkService.selectKakaoSentRefundListForSingle();
			Set<String> targetIdSet = new HashSet<>();
			
			for(KakaoVO kakaoVO : kakaoRefundList) {
				try {
					kakaoAlimTalkService.kakaoSingleRefund(kakaoVO);
					targetIdSet.add(kakaoVO.getUserId());
				} catch (Exception e) {
					String msg = "[문자온] 환불 실패 - " + kakaoVO.getMsgId() +"("+ kakaoVO.getUserId() + ")";
					mjonCommon.sendSimpleSlackMsg(msg);;
				}
			}
			
			MjonPayVO mjonPayVO = new MjonPayVO();
			for(String userId : targetIdSet) {
				try {
				mjonPayVO.setUserId(userId);
				mjonPayService.updateMemberCash(mjonPayVO); //회원정보 업데이트
				} catch(Exception e) {
					String msg = "[문자온] 환불 후 잔액 갱신 실패 - " + userId;
					mjonCommon.sendSimpleSlackMsg(msg);;
				}
			}
			
	}
	
	//대체문자 결과 반영
	@Scheduled(cron = "0 0/2 * * * ?") // 2분마다 실행
	@SchedulerLock(name = "updateKakaoResendResult", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
	public void runUpdateKakaoResendResult() throws Exception {
		
		try {
			System.out.println("=============SchedulerUtil=====runUpdateKakaoResendResult =============>");
			schdlrManageService.updateKakaoResendResult();
		}catch(Exception ex) {
			ex.printStackTrace();
		}	
		
	}
	
	//환불 실행
	private void PayBack(String p_type) throws Exception {
		
		
		// 문자 환불
		schdlrManageService.payBack_advc(p_type);
		
		/*
		if ("ONE".equals(p_type)) {	//하루에 한번만 31일 대상으로 실행
			schdlrManageService.msgFailPayBackOneByDay();	
		}else {	
			// 10분 마다 1주일 데이터에서 환불 대상을 찾아서 환불 한다.
			//어플리케이션 트랜잭션 문제가 있어 Util 단으로 DB 호출을 가져옴	-	2024-03-14
			//schdlrManageService.msgFailPayBack();
			
			List<MjonMsgVO> msgFailList = mjonMsgDataDAO.selectMsgSentFailList();
			
			long forBeforeTime = System.currentTimeMillis(); // 코드 실행 전 시간
			System.out.println("==  msgFailList  =============>  : "+ msgFailList.size());
			for(MjonMsgVO vo : msgFailList) {
				
				try {
					mjonMsgDataDAO.updateMsgSentFailPayBack(vo);
				}catch(Exception ex) {
					System.out.println("=============SchedulerUtil=====PayBack Catch =============>");
					System.out.println(vo.getUserId()+"  :   "+ vo.getMsgGroupId() +" :  "+ vo.getUserData());
					ex.printStackTrace();
				}
				
			}
			long forAfterTime = System.currentTimeMillis(); // 코드 실행 후 시간
			long forSecDiffTime = (forAfterTime - forBeforeTime)/1000; // 코드 실행 전후 시간 차이 계산(초 단위)
			System.out.println("==PayBack for 2 =============> 수량 : "+msgFailList.size()+"  =====  "  + forSecDiffTime +"초");
		}
			
		*/
		System.out.println("==== faxFailPayBack ====");
		// 팩스 환불
		schdlrManageService.faxFailPayBack();
			
	}
}
