package com.caliverse.admin.scheduler.polling.service;

import com.caliverse.admin.domain.entity.BlackList;
import com.caliverse.admin.domain.entity.log.LogAction;
import com.caliverse.admin.domain.entity.log.LogStatus;
import com.caliverse.admin.domain.service.BlackListService;
import com.caliverse.admin.domain.service.UserGameSessionService;
import com.caliverse.admin.global.common.annotation.BusinessProcess;
import com.caliverse.admin.global.component.manager.BusinessProcessIdManager;
import com.caliverse.admin.mongodb.service.BusinessLogService;
import com.caliverse.admin.scheduler.CommonScheduler;
import com.caliverse.admin.scheduler.entity.SchedulerType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
public class BlackListScheduler extends CommonScheduler {

    private final UserGameSessionService userGameSessionService;
    private final BlackListService blackListService;

    public BlackListScheduler(UserGameSessionService userGameSessionService,
                              BlackListService blackListService,
                              BusinessLogService businessLogService,
                              BusinessProcessIdManager processIdManager) {
        super(businessLogService, processIdManager);
        this.userGameSessionService = userGameSessionService;
        this.blackListService = blackListService;
    }

    @Override
    protected void executeInternal() {
        executeWithProcessId(LogAction.SCHEDULE_BLACK_LIST, this::executeLogic);

    }

    private void executeLogic() {
        List<BlackList> blackList = blackListService.getScheduleBlackList();

        LocalDateTime startTime = LocalDateTime.now();
        List<Long> processedBlackListIds = new ArrayList<>();
        AtomicInteger timeoutFailCount = new AtomicInteger(0);
        AtomicInteger blockStartedCount = new AtomicInteger(0);
        AtomicInteger blockExpiredCount = new AtomicInteger(0);

        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);

                        processedBlackListIds.add(blockUser.getId());
                        timeoutFailCount.incrementAndGet();
                        log.error("blackListJob blackListSchedule timeOut Fail id: {}, guid: {}", blockUser.getId(), blockUser.getGuid());
                    } else if(!nowDate.isBefore(blockUser.getStartDt())) {
                        // 시작시간 도달 - INPROGRESS 처리
                        userGameSessionService.kickUserSession(blockUser.getGuid(), "user block");
                        blackListService.updateScheduleBlockUser(blockUser, BlackList.STATUSTYPE.INPROGRESS);
                        blackListService.updateBlackListStatus(blockUser.getId(), BlackList.STATUSTYPE.INPROGRESS);

                        processedBlackListIds.add(blockUser.getId());
                        blockStartedCount.incrementAndGet();
                        log.info("blackListJob blackListSchedule block start id: {}, guid: {}", blockUser.getId(), blockUser.getGuid());
                    }
                }
                // 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);

                    processedBlackListIds.add(blockUser.getId());
                    blockExpiredCount.incrementAndGet();
                    log.info("blackListJob blackListSchedule block end id: {}, guid: {}", blockUser.getId(), blockUser.getGuid());
                }
            });

            if (!processedBlackListIds.isEmpty() && (timeoutFailCount.get() > 0 || blockStartedCount.get() > 0 || blockExpiredCount.get() > 0)) {
                businessLogService.logScheduler(
                        LogStatus.SUCCESS,
                        String.format("블랙리스트 스케줄 처리 완료: 총 %d건 처리 (타임아웃실패: %d, 제재시작: %d, 제재만료: %d)",
                                processedBlackListIds.size(),
                                timeoutFailCount.get(),
                                blockStartedCount.get(),
                                blockExpiredCount.get()),
                        "BLACKLIST_SCHEDULER",
                        startTime,
                        LocalDateTime.now(),
                        Map.of(
                                "totalBlackListCount", blackList.size(),
                                "processedCount", processedBlackListIds.size(),
                                "processedBlackListIds", processedBlackListIds,
                                "timeoutFailCount", timeoutFailCount.get(),
                                "blockStartedCount", blockStartedCount.get(),
                                "blockExpiredCount", blockExpiredCount.get()
                        )
                );

                log.info("BlackList scheduler completed: {} items processed out of {} total",
                        processedBlackListIds.size(), blackList.size());
            }
        }catch (Exception e){
            log.error("blackListSchedule Exception: {}", e.getMessage());
            throw e;
        }
    }

    @Override
    public SchedulerType getSchedulerType() {
        return SchedulerType.BLACKLIST;
    }
}
