package com.caliverse.admin.scheduler;

import com.caliverse.admin.domain.RabbitMq.MessageHandlerService;
import com.caliverse.admin.domain.RabbitMq.message.LanguageType;
import com.caliverse.admin.domain.RabbitMq.message.MailItem;
import com.caliverse.admin.domain.RabbitMq.message.OperationSystemMessage;
import com.caliverse.admin.domain.entity.*;
import com.caliverse.admin.dynamodb.entity.ELandAuctionResult;
import com.caliverse.admin.dynamodb.entity.ELandAuctionState;
import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionHighestBidUserAttrib;
import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionRegistryAttrib;
import com.caliverse.admin.domain.entity.redis.RedisLoginInfo;
import com.caliverse.admin.domain.service.*;
import com.caliverse.admin.dynamodb.entity.SystemMessage;
import com.caliverse.admin.dynamodb.service.DynamodbService;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.global.common.utils.ExcelUtils;
import com.caliverse.admin.global.common.utils.JsonUtils;
import com.caliverse.admin.redis.service.RedisUserInfoService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import lombok.*;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.caliverse.admin.global.common.utils.DynamodbUtil.getLanguageType;

@Service
@AllArgsConstructor
@Slf4j
public class DynamicScheduler {

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private final MailService mailService;
    private final NoticeService noticeService;
    private final BlackListService blackListService;
    private final EventService eventService;
    private final LandService landService;
    private final UserGameSessionService userGameSessionService;
    private final ScheduleService schedulerService;
    private final RedisUserInfoService redisUserInfoService;
    private final MessageHandlerService messageHandlerService;
    private final BattleEventService battleEventService;
    private final HistoryService historyService;

    private final DynamodbService dynamodbService;

    private final ExcelUtils excelUtils;
    private final ObjectMapper objectMapper;

    public void battleEventSchedule(){
        List<BattleEvent> eventList = battleEventService.getScheduleBattleEventList();

        try{
            eventList.forEach(event -> {
                BattleEvent.BATTLE_STATUS status = event.getStatus();
                LocalDateTime start_dt = event.getEventStartDt();
                LocalTime startTime = start_dt.toLocalTime();

                LocalDateTime now = LocalDateTime.now();
                LocalTime currentTime = LocalDateTime.now().toLocalTime();
                LocalDateTime end_dt = event.getEventEndDt();
                int operation_time = event.getEventOperationTime();

                LocalTime endTime = startTime.plusSeconds(operation_time);

                // 현재시간이 자정을 넘어간 경우(00:00 ~ endTime)라면 전날을 기준으로
                LocalDate baseDate;
                if (startTime.isAfter(endTime)) { // 시작시간이 종료시간보다 늦은 경우 (자정을 걸치는 경우)
                    if (currentTime.isBefore(endTime)) {
                        // 현재시간이 00:00 ~ endTime 사이면 전날 기준
                        baseDate = now.toLocalDate().minusDays(1);
                    } else if (!currentTime.isBefore(startTime)) {
                        // 현재시간이 startTime 이후면 현재 날짜 기준
                        baseDate = now.toLocalDate();
                    } else {
                        baseDate = now.toLocalDate();
                    }
                } else {
                    // 자정을 걸치지 않는 일반적인 경우
                    baseDate = now.toLocalDate();
                }

                //오늘자 기준 시작, 종료 시간
                LocalDateTime todayStart = LocalDateTime.of(baseDate, startTime);
                LocalDateTime todayEnd = todayStart.plusSeconds(operation_time);

                BattleEvent.BATTLE_STATUS change_status = null;

                Map<String,Object> map = new HashMap<>();
                map.put("id", event.getId());

                if(status.equals(BattleEvent.BATTLE_STATUS.REGISTER) || status.equals(BattleEvent.BATTLE_STATUS.WAIT)){
                    if(!now.isBefore(start_dt) && (!now.isBefore(todayStart) && !now.isAfter(todayEnd))){
                        log.info("battle event_id: {}, start_dt: {}, todayStart: {}, todayEnd: {} STATUS CHANGE RUNNING", event.getId(), start_dt, todayStart, todayEnd);
                        change_status = BattleEvent.BATTLE_STATUS.RUNNING;
                        map.put("status", change_status);
                        battleEventService.updateBattleEventStatus(map);
                    }
                }else if(status.equals(BattleEvent.BATTLE_STATUS.RUNNING)){
                    if(!(!now.isBefore(todayStart) && !now.isAfter(todayEnd))){
                        if(!baseDate.isBefore(end_dt.toLocalDate())){
                            change_status = BattleEvent.BATTLE_STATUS.END;
                        }else{
                            change_status = BattleEvent.BATTLE_STATUS.WAIT;
                        }
                        log.info("battle event_id: {}, start_dt: {}, end_dt: {}, todayStart: {}, todayEnd: {} STATUS CHANGE {}", event.getId(), start_dt, end_dt, todayStart, todayEnd, change_status);
                        map.put("status", change_status);
                        battleEventService.updateBattleEventStatus(map);
                    }
                }
            });
        }catch(Exception e){
            log.error("battleEventSchedule Exception: {}", e.getMessage());
        }
    }

    public void landAuctionSchedule(){
        List<LandAuction> auctionList = landService.getScheduleLandAuctionList();

        try{
            auctionList.forEach(auction -> {
                LocalDateTime nowDate = LocalDateTime.now();
                LocalDateTime reservationDate = auction.getResvStartDt();
                LocalDateTime auctionStartDate = auction.getAuctionStartDt();
                LocalDateTime auctionEndDate = auction.getAuctionEndDt();

                LandAuction.AUCTION_STATUS auction_status = auction.getStatus();

                //예약 시작시간이 되지않으면 체크하지 않는다
                if(nowDate.isBefore(reservationDate)){
                    return;
                }

                //예약일때 경매 시작시간이 되지않았다면 체크하지 않는다
                if(auction_status.equals(LandAuction.AUCTION_STATUS.RESV_START) && nowDate.isBefore(auctionStartDate)){
                    return;
                }

                // 비정상종료일때가 있다고 상시체크로 해달라고 함(23.02.18)
                // 경매중일때 경매 종료시간이 되지않았다면 체크하지 않는다
//                if(auction_status.equals(LandAuction.AUCTION_STATUS.AUCTION_START) && nowDate.isBefore(auctionEndDate)){
//                    return;
//                }

                Map<String,Object> map = new HashMap<>();
                map.put("id", auction.getId());

                LandAuctionRegistryAttrib attrib = landService.getLandAuctionRegistryAttrib(auction.getLandId(), auction.getAuctionSeq());
                String dynamodb_auction_state = attrib.getAuctionState();
                LandAuction.AUCTION_STATUS change_status = null;
                if(dynamodb_auction_state.equals(ELandAuctionState.SCHEDULED.getName())){
                    change_status = LandAuction.AUCTION_STATUS.RESV_START;
                    map.put("status", change_status);
                    landService.updateLandAuctionStatus(map);
                    log.info("land_auction id: {} Status Schedule Changed", auction.getId());
                }else if(dynamodb_auction_state.equals(ELandAuctionState.STARTED.getName())){
                    change_status = LandAuction.AUCTION_STATUS.AUCTION_START;
                    map.put("status", change_status);
                    landService.updateLandAuctionStatus(map);
                    log.info("land_auction id: {} Status Auction Start Changed", auction.getId());
                }else if(dynamodb_auction_state.equals(ELandAuctionState.ENDED.getName())){
                    String result = attrib.getAuctionResult();

                    if(result.equals(ELandAuctionResult.SUCCESSED.getName())){
                        LandAuctionHighestBidUserAttrib bidUser = landService.getLandAuctionHighestUserAttrib(auction.getLandId(), auction.getAuctionSeq());
                        if(bidUser == null) return;
                        log.info("land_auction id: {} Status SUCCESS Changed. bidUser: {}", auction.getId(), bidUser);
                        String close_dt = CommonUtils.convertIsoByDatetime(attrib.getProcessVersionTime());
                        change_status = LandAuction.AUCTION_STATUS.AUCTION_END;
                        map.put("status", change_status);
                        map.put("closeEndDt", close_dt);
                        map.put("bidderGuid", bidUser.getHighestBidUserGuid());
                        map.put("bidderNickname", bidUser.getHighestBidUserNickname());
                        map.put("closePrice", bidUser.getHighestBidPrice());
                        landService.updateLandAuctionStatus(map);
                    }else{
                        if(result.equals(ELandAuctionResult.CANCELED.getName())){
                            change_status = LandAuction.AUCTION_STATUS.CANCEL;
                            map.put("status", change_status);
                        }else{
                            change_status = LandAuction.AUCTION_STATUS.FAIL;
                            map.put("status", change_status);
                        }
                        log.info("land_auction id: {} Status {} Changed.", auction.getId(), change_status);
                        landService.updateLandAuctionStatus(map);
                    }
                }
            });
        } catch (Exception e){
            log.error("landAuctionSchedule Exception: {}", e.getMessage());
        }
    }

    public void eventSchedule(){
        List<Event> eventList = eventService.getScheduleMailList();

        try{
            eventList.forEach(event -> {
                LocalDateTime nowDate = LocalDateTime.now();
                LocalDateTime startTime = event.getStartDt();
                LocalDateTime endTime = event.getEndDt();

                if(nowDate.isAfter(endTime) && event.getStatus().equals(Event.EVENTSTATUS.WAIT)){
                    eventService.updateEventStatus(event.getId(), Event.EVENTSTATUS.FAIL);
                    log.error("eventJob eventSchedule timeOut : {}", event);
                    return;
                }

                // 시작시간이 30분 남았을때 게임DB에 insert 해준다
                if (event.getStatus().equals(Event.EVENTSTATUS.WAIT) && Duration.between(nowDate, startTime).abs().toMinutes() <= 30 && !event.isAddFlag()) {
//                    systemMailInsert(event);
                    eventService.insertSystemMail(event);
//                    eventService.updateEventStatus(event.getId(), Event.EVENTSTATUS.RUNNING);
                    log.info("eventJob eventSchedule dynamoDB Insert & Start: {}", event);
                }

//                if(!nowDate.isBefore(startTime) && event.getStatus().equals(Event.EVENTSTATUS.WAIT)){
//                    eventService.updateEventStatus(event.getId(), Event.EVENTSTATUS.RUNNING);
//                    log.info("eventJob eventSchedule block start : {}", event);
//                }

                if(!nowDate.isBefore(endTime) && event.getStatus().equals(Event.EVENTSTATUS.RUNNING)){
                    eventService.updateEventStatus(event.getId(), Event.EVENTSTATUS.FINISH);
                    log.info("eventJob eventSchedule block end : {}", event);
                }
            });
        }catch (Exception e){
            log.error("eventSchedule Exception: {}", e.getMessage());
        }
    }

    public void blackListSchedule(){
        List<BlackList> blackList = blackListService.getScheduleBlackList();

        try{
            LocalDateTime nowDate = LocalDateTime.now();
            blackList.forEach(blockUser -> {
                BlackList.STATUSTYPE status = blockUser.getStatus();

                // WAIT 상태 처리
                if(status == BlackList.STATUSTYPE.WAIT) {
                    if(nowDate.isAfter(blockUser.getEndDt())) {
                        // 만료시간 초과 - FAIL 처리
                        blackListService.updateBlackListStatus(blockUser.getId(), BlackList.STATUSTYPE.FAIL);
                        log.error("blackListJob blackListSchedule timeOut : {}", blockUser);
                    } else if(!nowDate.isBefore(blockUser.getStartDt())) {
                        // 시작시간 도달 - INPROGRESS 처리
                        userGameSessionService.kickUserSession(blockUser.getGuid());
                        blackListService.updateScheduleBlockUser(blockUser, BlackList.STATUSTYPE.INPROGRESS);
                        blackListService.updateBlackListStatus(blockUser.getId(), BlackList.STATUSTYPE.INPROGRESS);
                        log.info("blackListJob blackListSchedule block start : {}", blockUser);
                    }
                }
                // INPROGRESS 상태 처리
                else if(status == BlackList.STATUSTYPE.INPROGRESS && !nowDate.isBefore(blockUser.getEndDt())) {
                    // 만료시간 도달 - EXPIRATION 처리
                    blackListService.updateScheduleBlockUser(blockUser, BlackList.STATUSTYPE.EXPIRATION);
                    blackListService.updateBlackListStatus(blockUser.getId(), BlackList.STATUSTYPE.EXPIRATION);
                    log.info("blackListJob blackListSchedule block end : {}", blockUser);
                }
//                LocalDateTime startTime = blockUser.getStartDt();
//                LocalDateTime endTime = blockUser.getEndDt();
//
//                // 상태가 대기중이고 만료시간이 지났으면 실패 처리
//                if(nowDate.isAfter(endTime) && blockUser.getStatus().equals(BlackList.STATUSTYPE.WAIT)){
//                    blackListService.updateBlackListStatus(blockUser.getId(), BlackList.STATUSTYPE.FAIL);
//                    log.error("blackListJob blackListSchedule timeOut : {}", blockUser);
//                    return;
//                }
//
//                // 상태가 대기중이고 시작시간 지났으면 유저제재 처리
//                if(!nowDate.isBefore(startTime) && blockUser.getStatus().equals(BlackList.STATUSTYPE.WAIT)){
//                    // user kick 처리
//                    userGameSessionService.kickUserSession(blockUser.getGuid());
//                    blackListService.updateScheduleBlockUser(blockUser, BlackList.STATUSTYPE.INPROGRESS);
//                    blackListService.updateBlackListStatus(blockUser.getId(), BlackList.STATUSTYPE.INPROGRESS);
//                    log.info("blackListJob blackListSchedule block start : {}", blockUser);
//                }
//
//                // 상태가 제재중이고 만료시간 지났으면 상태 종료 처리
//                if(!nowDate.isBefore(endTime) && blockUser.getStatus().equals(BlackList.STATUSTYPE.INPROGRESS)){
//                    blackListService.updateScheduleBlockUser(blockUser, BlackList.STATUSTYPE.EXPIRATION);
//                    blackListService.updateBlackListStatus(blockUser.getId(), BlackList.STATUSTYPE.EXPIRATION);
//                    log.info("blackListJob blackListSchedule block end : {}", blockUser);
//                }
            });
        }catch (Exception e){
            log.error("blackListSchedule Exception: {}", e.getMessage());
        }

    }

    public void mailSchedule() {
        List<Mail> mailList = mailService.getScheduleMailList();

        try{
            mailList.forEach(mail -> {
                if(mail.getSendStatus().equals(Mail.SENDSTATUS.WAIT)){
                    if(mail.getSendType() == Mail.SENDTYPE.DIRECT_SEND){
                        log.info("mailJob mailSchedule direct run : {}", mail);
                        mailTypeProcess(mail);
                    }else {
                        String key = "mail-" + mail.getId();
                        if (!schedulerService.isTaskExist(key)) {
                            Runnable taskRunner = () -> {
                                log.info("mailJob mailSchedule schedule run : {}", mail);
                                mailTypeProcess(mail);
                                // 스케줄 해제
                                schedulerService.closeTask(key);
                            };

                            // 시작일자 - 현재일자
                            LocalDateTime startDate = mail.getSendDt();
                            long initialDelay = Duration.between(LocalDateTime.now(), startDate).toMillis();

                            log.info("mailJob mailSchedule schedule add : {}", mail);
                            ScheduledFuture<?> schedule = scheduler.schedule(taskRunner, initialDelay, TimeUnit.MILLISECONDS);
                            schedulerService.addTask(key, schedule);
                        }
                    }
                }
            });
        }catch (Exception e){
            log.error("mailSchedule Exception: {}", e.getMessage());
            mailService.setScheduleLog(HISTORYTYPE.SCHEDULE_MAIL_FAIL, e.getMessage());
        }
    }

    public void noticeSchedule() {
        List<InGame> noticeList = noticeService.getScheduleNoticeList();

        try{
            noticeList.forEach(notice -> {
                String key = "notice-" + notice.getId();
                if (!schedulerService.isTaskExist(key)) {
                    Runnable taskRunner = new Runnable() {
                        private int count = notice.getSendCnt().intValue();
                        private final int repeat_cnt = notice.getRepeatCnt().intValue();
                        private final boolean isRepeat = notice.getIsRepeat();
                        private final long id = notice.getId();
                        private final String runKey = key;
                        private final InGame.REPEATTYPE repeatType = notice.getRepeatType();
                        private final LocalDateTime endDate = notice.getEndDt();

                        @Override
                        public void run() {
                            log.info("noticeJob noticeSchedule schedule run : {}", notice);

                            boolean is_send = noticeSend(notice);
                            if(!is_send) return;
                            noticeService.updateNoticeCount(id);

                            // 반복여부
                            if(isRepeat){
                                // 반복타입
                                switch(repeatType){
                                    // 횟수
                                    case COUNT -> {
                                        count++;
                                        // 정해진 횟수만큼 돌았으면 종료
                                        if(count >= repeat_cnt){
                                            noticeService.updateNoticeStatus(id, InGame.SENDSTATUS.FINISH);
                                            schedulerService.closeTask(runKey);
                                            log.info("Notice Schedule End RepeatType: Count, id: {}", id);
                                        }
                                    }
                                    // 일자
                                    case DATE -> {
                                        if (LocalDateTime.now().isAfter(endDate)) {
                                            noticeService.updateNoticeStatus(id, InGame.SENDSTATUS.FINISH);
                                            schedulerService.closeTask(runKey);
                                            log.info("Notice Schedule End RepeatType: Date, id: {}", id);
                                        }
                                    }
                                }
                            }else{
                                // 스케줄 해제 및 상태 변경
                                noticeService.updateNoticeStatus(id, InGame.SENDSTATUS.FINISH);
                                schedulerService.closeTask(runKey);
                                log.info("Notice One Time Schedule End id: {}", id);
                            }
                        }
                    };

                    // 반복여부 체크
                    if(notice.getIsRepeat()){
                        // 반복타입
                        switch(notice.getRepeatType()){
                            // 횟수
                            case COUNT ->{
                                // 시작일자 - 현재일자
                                LocalDateTime startDate = notice.getSendDt();
                                long initialDelay = Duration.between(LocalDateTime.now(), startDate).toMillis();    // 시작시간
                                long period = LocalTime.parse(notice.getRepeatDt()).toSecondOfDay();    // 반복주기

                                ScheduledFuture<?> schedule = scheduler.scheduleAtFixedRate(taskRunner, initialDelay, period, TimeUnit.MILLISECONDS);
                                schedulerService.addTask(key, schedule);
                                noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.RUNNING);
                            }
                            // 일자
                            case DATE -> {
                                LocalDateTime startDate = notice.getSendDt();
                                long initialDelay = Duration.between(LocalDateTime.now(), startDate).toMillis();    // 시작시간
                                long period = CommonUtils.intervalToMillis(notice.getRepeatDt());   // 반복주기

                                ScheduledFuture<?> schedule = scheduler.scheduleAtFixedRate(taskRunner, initialDelay, period, TimeUnit.MILLISECONDS);
                                schedulerService.addTask(key, schedule);
                                noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.RUNNING);
                            }
                            // 특정시간
                            case TIME -> {
                                LocalDateTime now_date = LocalDateTime.now();
                                LocalDateTime start_date = notice.getSendDt();

                                if(now_date.isAfter(start_date)) {
                                    LocalTime now_time = LocalTime.now();
                                    LocalTime repeat_time = CommonUtils.stringToTime(notice.getRepeatDt());
                                    LocalDateTime end_date = notice.getEndDt();
                                    InGame.SENDSTATUS status = notice.getSendStatus();
                                    Long id = notice.getId();

                                    if(status.equals(InGame.SENDSTATUS.WAIT)) noticeService.updateNoticeStatus(id, InGame.SENDSTATUS.RUNNING);

                                    if (now_time.equals(repeat_time)) {
                                        log.info("noticeJob noticeSchedule RepeatType: Time run : {}", notice);
                                        boolean is_send = noticeSend(notice);
                                        if (!is_send) {
                                            log.error("Notice Schedule Fail RepeatType: Time, id: {}", id);
                                        }
                                    }

                                    if(status.equals(InGame.SENDSTATUS.RUNNING) && (now_date.isBefore(end_date) || now_date.isEqual(end_date))){
                                        noticeService.updateNoticeStatus(id, InGame.SENDSTATUS.FINISH);
                                        log.info("Notice Schedule End RepeatType: Time, id: {}", id);
                                    }
                                }
                            }
                        }

                    }else{
                        // 시작일자 - 현재일자
                        LocalDateTime startDate = notice.getSendDt();
                        long initialDelay = Duration.between(LocalDateTime.now(), startDate).toMillis();

                        if(initialDelay < 0) {
                            log.error("noticeJob noticeSchedule schedule add fail time over");
                            noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.FAIL);
                            mailService.setScheduleLog(HISTORYTYPE.SCHEDULE_NOTICE_FAIL, "시작일자가 현재일자보다 이전으로 송출 중단.");
                            return;
                        }

                        log.info("noticeJob noticeSchedule schedule add : {}", notice);
                        ScheduledFuture<?> schedule = scheduler.schedule(taskRunner, initialDelay, TimeUnit.MILLISECONDS);
                        schedulerService.addTask(key, schedule);
                    }
                }
            });
        }catch (Exception e){
            log.error("noticeSchedule Exception: {}", e.getMessage());
            noticeService.setScheduleLog(HISTORYTYPE.SCHEDULE_NOTICE_FAIL, e.getMessage());
        }
    }

    private void mailTypeProcess(Mail mail){
        if(mail.getReceiveType().equals(Mail.RECEIVETYPE.SINGLE)){
            boolean is_send = mainSend(mail);
            if(is_send) mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FINISH);
        }
        else{
            List<Excel> excelList = excelUtils.getExcelListData(mail.getTarget());
            log.info("mailJob mailSchedule schedule run ExcelList : {}", excelList);
            boolean is_send;
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.RUNNING);
            for(Excel excel : excelList){
                String guid = mailService.getGuid(excel.getUser(), excel.getType());
                mail.setTarget(guid);
                is_send = mainSend(mail);
                if(!is_send){
                    log.error("mailJob mailSchedule Excel fail user : {}", excel.getUser());
                    mailService.setScheduleLog(HISTORYTYPE.SCHEDULE_MAIL_FAIL, "mail schedule id: " + mail.getId() + " Excel Send Fail User: " + excel.getUser());
                }
                else log.info("mailJob mailSchedule Excel send user : {}", excel.getUser());
            }
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FINISH);
        }
    }

    // 우편 전송
    private boolean mainSend(Mail mail){
        // 서버정보 가져오기
        try{
            RedisLoginInfo info = redisUserInfoService.getUserLoginSessionInfo(mail.getTarget());
            String server_name = null;
            if(info == null){
                server_name = redisUserInfoService.getFirstChannel();
                if(server_name == null){
                    log.error("mailJob mainSend serverName is empty");
                    mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FAIL);
                    mailService.setScheduleLog(HISTORYTYPE.MAIL_SEND_FAIL, "is null server name");
                    return false;
                }
            }else{
                server_name = info.getCurrentServer();
            }

            // 메시지 처리
            List<Message> msgList = mailService.getMailMessageList(mail.getId());
            List<OperationSystemMessage> titleList = new ArrayList<>();
            List<OperationSystemMessage> contentList = new ArrayList<>();
            List<OperationSystemMessage> senderList = new ArrayList<>();
            for(Message msg : msgList){
                LanguageType lang = null;
                String langText = msg.getLanguage();
                String sender = null;

                if(langText.equals(LANGUAGETYPE.EN.toString())){
                    lang = LanguageType.LanguageType_en;
                    sender = "Administrator";
                }else if(langText.equals(LANGUAGETYPE.JA.toString())){
                    lang = LanguageType.LanguageType_ja;
                    sender = "アドミニストレーター";
                }else{
                    lang = LanguageType.LanguageType_ko;
                    sender = "시스템 관리자";
                }
                titleList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(msg.getTitle())).build());
                contentList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(msg.getContent())).build());
                senderList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(sender)).build());
            }

            // 아이템 처리
            List<Item> itemList = mailService.getMailItemList(mail.getId());
            List<MailItem> mailItemList = new ArrayList<>();
            for(Item item : itemList){
                mailItemList.add(MailItem.newBuilder()
                        .setItemId(CommonUtils.stringToInt(item.getItem()))
                        .setCount(item.getItemCnt()).build());
            }

            log.info("Send Mail Message : {}, target : {}, type :{}", contentList, mail.getTarget(), mail.getMailType());
            messageHandlerService.sendMailMessage(server_name, mail.getTarget(), mail.getMailType().toString(), titleList, contentList, mailItemList, senderList);
            log.info("mailJob mailSend completed");
            return true;
        }catch(Exception e){
            log.error("mailSend Exception: {}", e.getMessage());
            mailService.updateMailStatus(mail.getId(), Mail.SENDSTATUS.FAIL);
            mailService.setScheduleLog(HISTORYTYPE.MAIL_SEND_FAIL, e.getMessage());
        }
        return false;
    }

    // 공지사항 전송
    private boolean noticeSend(InGame notice){
        try{
            //redis 채널 및 인던 정보 가져오기
            List<String> serverList = redisUserInfoService.getAllServerList();
            if(serverList.isEmpty()){
                log.error("noticeJob noticeSend serverList is empty");
                noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.FAIL);
                noticeService.setScheduleLog(HISTORYTYPE.NOTICE_SEND_FAIL, "is null server name");
                return false;
            }

            //메시지 처리
            List<Message> msgList = noticeService.getNoticeMessageList(notice.getId());
            List<OperationSystemMessage> contentList = new ArrayList<>();
            List<OperationSystemMessage> senderList = new ArrayList<>();
            for(Message msg : Collections.unmodifiableList(msgList)){
                LanguageType lang = null;
                String langText = msg.getLanguage();
                String sender = null;

                if(langText.equals(LANGUAGETYPE.EN.toString())){
                    lang = LanguageType.LanguageType_en;
                    sender = "Administrator";
                }else if(langText.equals(LANGUAGETYPE.JA.toString())){
                    lang = LanguageType.LanguageType_ja;
                    sender = "アドミニストレーター";
                }else{
                    lang = LanguageType.LanguageType_ko;
                    sender = "시스템 관리자";
                }
                contentList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(msg.getContent())).build());
                senderList.add(OperationSystemMessage.newBuilder()
                        .setLanguageType(lang)
                        .setText(CommonUtils.stringToByte(sender)).build());
            }

            log.info("Send Notice Message: {}, type: {}", contentList, notice.getMessageType());
            messageHandlerService.sendNoticeMessage(serverList, notice.getMessageType().toString(), contentList, senderList);
            log.info("noticeJob noticeSend completed");
            return true;
        }catch (Exception e){
            log.error("noticeSend Exception: {}", e.getMessage());
            noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.FAIL);
            noticeService.setScheduleLog(HISTORYTYPE.NOTICE_SEND_FAIL, e.getMessage());
        }
        return false;
    }

    // 시스템 우편 등록
//    private void systemMailInsert(Event event){
//        try {
//            log.info("systemMailInsert Info: {}", event);
//            ObjectMapper objectMapper = new ObjectMapper();
//            ArrayNode mailTitleArray = objectMapper.createArrayNode();
//            ArrayNode mailTextArray = objectMapper.createArrayNode();
//            ArrayNode mailSenderArray = objectMapper.createArrayNode();
//            ArrayNode mailItemArray = objectMapper.createArrayNode();
//
//            List<Message> msgList = eventService.getMessageList(event.getId());
//            List<Item> itemList = eventService.getItemList(event.getId());
//            for (Message msg : msgList) {
//                String langText = msg.getLanguage();
//                int lang;
//                String sender;
//
//                if (langText.equals(LANGUAGETYPE.EN.toString())) {
//                    lang = LanguageType.LanguageType_en.getNumber();
//                    sender = "CALIVERSE";
//                } else if (langText.equals(LANGUAGETYPE.JA.toString())) {
//                    lang = LanguageType.LanguageType_ja.getNumber();
//                    sender = "カリバース";
//                } else {
//                    lang = LanguageType.LanguageType_ko.getNumber();
//                    sender = "칼리버스";
//                }
//                SystemMessage titleMessage = SystemMessage.builder().LanguageType(lang).Text(msg.getTitle()).build();
//                SystemMessage textMessage = SystemMessage.builder().LanguageType(lang).Text(msg.getContent()).build();
//                SystemMessage senderMessage = SystemMessage.builder().LanguageType(lang).Text(sender).build();
//
//                mailTitleArray.add(JsonUtils.createSystemMessage(titleMessage));
//                mailTextArray.add(JsonUtils.createSystemMessage(textMessage));
//                mailSenderArray.add(JsonUtils.createSystemMessage(senderMessage));
//            }
//            for (Item item : itemList) {
//                MailItem mailItem = MailItem.newBuilder().setItemId(CommonUtils.stringToInt(item.getItem())).setCount(item.getItemCnt()).build();
//                mailItemArray.add(JsonUtils.createMAilItem(mailItem));
//            }
//
//            eventService.insertSystemMail(event, mailTitleArray, mailTextArray, mailSenderArray, mailItemArray);
//
//        }catch (Exception e){
//            log.error("systemMailInsert Exception: {}", e.getMessage());
//            eventService.updateEventStatus(event.getId(), Event.EVENTSTATUS.FAIL);
//            historyService.setScheduleLog(HISTORYTYPE.SCHEDULE_EVENT_FAIL, e.getMessage());
//        }
//    }

}
