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

import com.caliverse.admin.domain.entity.Event;
import com.caliverse.admin.domain.entity.log.LogAction;
import com.caliverse.admin.domain.entity.log.LogStatus;
import com.caliverse.admin.domain.service.EventService;
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.Duration;
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 EventScheduler extends CommonScheduler {
    private final EventService eventService;

    public EventScheduler(EventService eventService,
                          BusinessLogService businessLogService,
                          BusinessProcessIdManager processIdManager) {
        super(businessLogService, processIdManager);
        this.eventService = eventService;
    }

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

    private void executeLogic() {
        List<Event> eventList = eventService.getScheduleMailList();

        LocalDateTime startTime = LocalDateTime.now();
        List<Long> processedEventIds = new ArrayList<>();
        AtomicInteger timeoutCount = new AtomicInteger(0);
        AtomicInteger systemMailInsertedCount = new AtomicInteger(0);
        AtomicInteger finishedCount = new AtomicInteger(0);

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

                if(nowDate.isAfter(endTime) && event.getStatus().equals(Event.EVENTSTATUS.WAIT)){
                    eventService.updateEventStatus(event.getId(), Event.EVENTSTATUS.FAIL);

                    processedEventIds.add(event.getId());
                    timeoutCount.incrementAndGet();
                    log.error("eventJob eventSchedule timeOut : {}", event);
                    return;
                }

                // 시작시간이 30분 남았을때 게임DB에 insert 해준다(서버에서 캐시 등록)
                if (event.getStatus().equals(Event.EVENTSTATUS.WAIT) && Duration.between(nowDate, eventStartTime ).abs().toMinutes() <= 30 && !event.isAddFlag()) {
                    eventService.insertSystemMail(event);

                    processedEventIds.add(event.getId());
                    systemMailInsertedCount.incrementAndGet();
                    log.info("eventJob eventSchedule dynamoDB Insert & Start: {}", event);
                }

                if(!nowDate.isBefore(endTime) && event.getStatus().equals(Event.EVENTSTATUS.RUNNING)){
                    eventService.updateEventStatus(event.getId(), Event.EVENTSTATUS.FINISH);

                    processedEventIds.add(event.getId());
                    finishedCount.incrementAndGet();
                    log.info("eventJob eventSchedule block end : {}", event);
                }
            });

            if (!processedEventIds.isEmpty() && (timeoutCount.get() > 0 || systemMailInsertedCount.get() > 0 || finishedCount.get() > 0)) {
                businessLogService.logScheduler(
                        LogStatus.SUCCESS,
                        String.format("이벤트 스케줄 처리 완료: 총 %d건 처리 (타임아웃: %d, 시스템메일등록: %d, 종료: %d)",
                                processedEventIds.size(),
                                timeoutCount.get(),
                                systemMailInsertedCount.get(),
                                finishedCount.get()),
                        "EVENT_SCHEDULER",
                        startTime,
                        LocalDateTime.now(),
                        Map.of(
                                "totalEventCount", eventList.size(),
                                "processedCount", processedEventIds.size(),
                                "processedEventIds", processedEventIds,
                                "timeoutCount", timeoutCount.get(),
                                "systemMailInsertedCount", systemMailInsertedCount.get(),
                                "finishedCount", finishedCount.get()
                        )
                );

                log.info("Event scheduler completed: {} items processed out of {} total",
                        processedEventIds.size(), eventList.size());
            }
        }catch (Exception e){
            log.error("eventSchedule Exception: {}", e.getMessage());
            throw e;
        }
    }

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