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

import com.caliverse.admin.domain.entity.LandAuction;
import com.caliverse.admin.domain.entity.log.LogAction;
import com.caliverse.admin.domain.entity.log.LogStatus;
import com.caliverse.admin.domain.service.LandService;
import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionHighestBidUserAttrib;
import com.caliverse.admin.dynamodb.domain.atrrib.LandAuctionRegistryAttrib;
import com.caliverse.admin.dynamodb.entity.ELandAuctionResult;
import com.caliverse.admin.dynamodb.entity.ELandAuctionState;
import com.caliverse.admin.global.common.annotation.BusinessProcess;
import com.caliverse.admin.global.common.utils.CommonUtils;
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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
public class LandAuctionScheduler extends CommonScheduler {
    private final LandService landService;

    public LandAuctionScheduler(LandService landService,
                                BusinessLogService businessLogService,
                                BusinessProcessIdManager processIdManager) {
        super(businessLogService, processIdManager);
        this.landService = landService;
    }

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

    private void executeLogic() {
        List<LandAuction> auctionList = landService.getScheduleLandAuctionList();

        LocalDateTime startTime = LocalDateTime.now();
        List<Long> processedAuctionIds = new ArrayList<>();
        AtomicInteger scheduledCount = new AtomicInteger(0);
        AtomicInteger startedCount = new AtomicInteger(0);
        AtomicInteger endedSuccessCount = new AtomicInteger(0);
        AtomicInteger endedFailCount = new AtomicInteger(0);
        AtomicInteger canceledCount = new AtomicInteger(0);

        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());
                if(attrib == null) return;
                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);

                    processedAuctionIds.add(auction.getId());
                    scheduledCount.incrementAndGet();
                    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);

                    processedAuctionIds.add(auction.getId());
                    startedCount.incrementAndGet();
                    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);

                        processedAuctionIds.add(auction.getId());
                        endedSuccessCount.incrementAndGet();
                    }else{
                        processedAuctionIds.add(auction.getId());

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

            if (!processedAuctionIds.isEmpty() && (scheduledCount.get() > 0 || startedCount.get() > 0 || endedSuccessCount.get() > 0 || endedFailCount.get() > 0 || canceledCount.get() > 0)) {
                businessLogService.logScheduler(
                        LogStatus.SUCCESS,
                        String.format("토지 경매 스케줄 처리 완료: 총 %d건 처리 (예약시작: %d, 경매시작: %d, 성공종료: %d, 실패종료: %d, 취소: %d)",
                                processedAuctionIds.size(),
                                scheduledCount.get(),
                                startedCount.get(),
                                endedSuccessCount.get(),
                                endedFailCount.get(),
                                canceledCount.get()),
                        "LAND_AUCTION_SCHEDULER",
                        startTime,
                        LocalDateTime.now(),
                        Map.of(
                                "totalAuctionCount", auctionList.size(),
                                "processedCount", processedAuctionIds.size(),
                                "processedAuctionIds", processedAuctionIds,
                                "scheduledCount", scheduledCount.get(),
                                "startedCount", startedCount.get(),
                                "endedSuccessCount", endedSuccessCount.get(),
                                "endedFailCount", endedFailCount.get(),
                                "canceledCount", canceledCount.get()
                        )
                );

                log.info("Land auction scheduler completed: {} items processed out of {} total",
                        processedAuctionIds.size(), auctionList.size());
            }
        } catch (Exception e){
            log.error("landAuctionSchedule Exception: {}", e.getMessage());
            throw e;
        }
    }

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