package com.caliverse.admin.scheduler.service;

import com.caliverse.admin.domain.RabbitMq.MessageHandlerService;
import com.caliverse.admin.domain.RabbitMq.message.LanguageType;
import com.caliverse.admin.domain.RabbitMq.message.OperationSystemMessage;
import com.caliverse.admin.domain.entity.HISTORYTYPE;
import com.caliverse.admin.domain.entity.InGame;
import com.caliverse.admin.domain.entity.Message;
import com.caliverse.admin.domain.service.NoticeService;
import com.caliverse.admin.redis.service.RedisUserInfoService;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.scheduler.config.ScheduleExecutionConfig;
import com.caliverse.admin.scheduler.ScheduleType;
import com.caliverse.admin.scheduler.Scheduler;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.time.Duration;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NoticeScheduler implements Scheduler {
    private InGame notice;
    private NoticeService noticeService;
    private RedisUserInfoService redisUserInfoService;
    private MessageHandlerService messageHandlerService;

    private final AtomicInteger currentExecutionCount = new AtomicInteger(0);  // 현재 실행 횟수를 추적

    @Override
    public void execute() {
        try {
            // 실행 전 횟수 체크
            if (shouldStopExecution()) {
                log.info("Notice execution stopped - Reached maximum count. NoticeId: {}, Current count: {}, Max count: {}",
                        notice.getId(), getCurrentCount(), getMaxCount());
                noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.FINISH);
                return;
            }

            // 실행 횟수 증가 및 검증
            int newCount = currentExecutionCount.incrementAndGet();
            if (newCount > getMaxCount()) {
                log.warn("Notice execution exceeded max count. NoticeId: {}, Current count: {}, Max count: {}",
                        notice.getId(), newCount, getMaxCount());
                noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.FINISH);
                return;
            }

            boolean isSend = noticeSend();
            if (!isSend) {
                // 전송 실패 시 카운트 롤백
                currentExecutionCount.decrementAndGet();
                return;
            }

            if(notice.getSendStatus().equals(InGame.SENDSTATUS.WAIT)) {
                noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.RUNNING);
            }

            // DB 업데이트 전 한번 더 체크
            if (newCount <= getMaxCount()) {
                noticeService.updateNoticeCount(notice.getId());
                log.info("Notice execution successful. NoticeId: {}, Current count: {}, Max count: {}",
                        notice.getId(), newCount, getMaxCount());

                // 최대 횟수 도달 시 상태 업데이트
                if (newCount >= getMaxCount()) {
                    noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.FINISH);
                    log.info("Notice completed all executions. NoticeId: {}", notice.getId());
                }
            }

        } catch (Exception e) {
            currentExecutionCount.decrementAndGet();
            log.error("Notice execution failed for notice ID: {}", notice.getId(), e);
            noticeService.updateNoticeStatus(notice.getId(), InGame.SENDSTATUS.FAIL);
            noticeService.setScheduleLog(HISTORYTYPE.SCHEDULE_NOTICE_FAIL, e.getMessage());
        }
    }

    private boolean shouldStopExecution() {
        if (!notice.getIsRepeat() || notice.getRepeatType() != InGame.REPEATTYPE.COUNT) {
            return false;
        }
        return getCurrentCount() >= getMaxCount();
    }

    private int getCurrentCount() {
        return currentExecutionCount.get();
    }

    private int getMaxCount() {
        return notice.getRepeatCnt().intValue();
    }

    @Override
    public boolean isRepeatable() {
        return notice.getIsRepeat();
    }

    @Override
    public ScheduleType getScheduleType() {
        if (!notice.getIsRepeat()) {
            return ScheduleType.DELAYED;
        }

        return switch (notice.getRepeatType()) {
            case COUNT, DATE -> ScheduleType.RECURRING;
            case TIME -> ScheduleType.PERIODIC;
            default -> ScheduleType.DELAYED;
        };
    }

    @Override
    public ScheduleExecutionConfig getSchedulerConfig() {
        return ScheduleExecutionConfig.builder()
                .taskId("notice-" + notice.getId())
                .startTime(notice.getSendDt())
                .endTime(notice.getEndDt())
                .interval(getRepeatInterval())
                .repeatCount(notice.getRepeatCnt().intValue())
                .build();
    }

    private Duration getRepeatInterval() {
        if (!notice.getIsRepeat()) {
            return null;
        }

        return switch (notice.getRepeatType()) {
            case COUNT -> Duration.ofSeconds(LocalTime.parse(notice.getRepeatDt()).toSecondOfDay());
            case DATE -> Duration.ofMillis(CommonUtils.intervalToMillis(notice.getRepeatDt()));
            case TIME -> null;
        };
    }

    @PostConstruct
    private void initialize() {
        // 기존 실행 횟수로 초기화
        currentExecutionCount.set(notice.getSendCnt().intValue());
    }

    // SchedulerManager에서 사용할 수 있도록 현재 실행 상태 제공
    public boolean hasReachedMaxExecutions() {
        return getCurrentCount() >= getMaxCount();
    }

    // 실행 횟수 검증을 위한 메서드
    public boolean isValidExecutionCount() {
        return getCurrentCount() <= getMaxCount();
    }

    private boolean noticeSend() {
        try {
            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;
                String sender;

                switch (msg.getLanguage()) {
                    case "EN" -> {
                        lang = LanguageType.LanguageType_en;
                        sender = "Administrator";
                    }
                    case "JA" -> {
                        lang = LanguageType.LanguageType_ja;
                        sender = "アドミニストレーター";
                    }
                    default -> {
                        lang = LanguageType.LanguageType_ko;
                        sender = "시스템 관리자";
                    }
                }

                contentList.add(createOperationSystemMessage(lang, msg.getContent()));
                senderList.add(createOperationSystemMessage(lang, sender));
            }

            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 OperationSystemMessage createOperationSystemMessage(LanguageType lang, String text) {
        return OperationSystemMessage.newBuilder()
                .setLanguageType(lang)
                .setText(CommonUtils.stringToByte(text))
                .build();
    }
}
