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

import com.caliverse.admin.domain.entity.BattleEvent;
import com.caliverse.admin.domain.entity.log.LogAction;
import com.caliverse.admin.domain.entity.log.LogStatus;
import com.caliverse.admin.domain.service.BattleEventService;
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.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
public class BattleEventScheduler extends CommonScheduler {
    private final BattleEventService battleEventService;

    public BattleEventScheduler(BattleEventService battleEventService,
                                BusinessLogService businessLogService,
                                BusinessProcessIdManager processIdManager) {
        super(businessLogService, processIdManager);
        this.battleEventService = battleEventService;
    }

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

    private void executeLogic(){
        List<BattleEvent> eventList = battleEventService.getScheduleBattleEventList();

        LocalDateTime startTime = LocalDateTime.now();
        List<Long> eventIds = new ArrayList<>();
        AtomicInteger processCount = new AtomicInteger(0);

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

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

                LocalTime endTimeOfDay = startTimeOfDay .plusSeconds(operation_time);

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

                //오늘자 기준 시작, 종료 시간
                LocalDateTime todayStart = LocalDateTime.of(baseDate, startTimeOfDay );
                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);

                        eventIds.add(event.getId());
                        processCount.incrementAndGet();
                    }
                }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);

                        eventIds.add(event.getId());
                        processCount.incrementAndGet();
                    }
                } else if(status.equals(BattleEvent.BATTLE_STATUS.STOP)){
                    if(!baseDate.isBefore(end_dt.toLocalDate())){
                        change_status = BattleEvent.BATTLE_STATUS.END;

                        log.info("battle event_id: {}, end_dt: {}, baseDate: {} STATUS CHANGE END (from STOP)",
                                event.getId(), end_dt, baseDate);
                        map.put("status", change_status);
                        battleEventService.updateBattleEventStatus(map);

                        eventIds.add(event.getId());
                        processCount.incrementAndGet();
                    }
                }
            });

            if (!eventIds.isEmpty() && (processCount.get() > 0) ) {
                businessLogService.logScheduler(
                        LogStatus.SUCCESS,
                        String.format("배틀 이벤트 스케줄 처리 완료: 총 %d건 (처리: %d)",
                                eventIds.size(),
                                processCount.get()),
                        "BATTLE_EVENT_SCHEDULER",
                        startTime,
                        LocalDateTime.now(),
                        Map.of(
                                "totalEventCount", eventList.size(),
                                "processedCount", eventIds.size(),
                                "processedEventIds", eventIds,
                                "processedComplete", processCount.get()
                        )
                );

                log.info("Battle event scheduler completed: {} items processed out of {} total",
                        eventIds.size(), eventList.size());
            }
        }catch(Exception e){
            log.error("battleEventSchedule Exception: {}", e.getMessage());
            throw e;
        }
    }

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