package com.utic.center.utic.traf.server.service;

import com.utic.center.common.annotation.ProcessingElapsed;
import com.utic.center.common.service.AbstractProcessService;
import com.utic.center.common.utils.LogUtils;
import com.utic.center.utic.traf.server.config.TraceConfig;
import com.utic.center.utic.traf.server.dto.LinkCollectParam;
import com.utic.center.utic.traf.server.dto.LinkCollectResultDto;
import com.utic.center.utic.traf.server.dto.LinkCollectSetupDto;
import com.utic.center.utic.traf.server.repository.ApplicationRepository;
import com.utic.center.utic.traf.server.service.worker.LinkTrafCollectWorker;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Slf4j
@Data
@ToString
@RequiredArgsConstructor
@Service
public class LinkTrafCollectService implements AbstractProcessService {

    public static final String OPER = "OPER";
    public static final String PRIO = "PRIO";
    public static final String PRIOETC = "PRIOETC";
    public static final String MOCT = "MOCT";

    private final TraceConfig trace;
    private final ConcurrentHashMap<String, LinkCollectSetupDto> clctStupMap = new ConcurrentHashMap<>();

    private ConcurrentHashMap<String, LinkCollectResultDto> mapData = new ConcurrentHashMap<>();

    @PostConstruct
    void init() {
        LinkCollectSetupDto oper = LinkCollectSetupDto.builder().clctSystCd(OPER).clctSystNm("운영자입력 소통정보").build();
        LinkCollectSetupDto prio = LinkCollectSetupDto.builder().clctSystCd(PRIO).clctSystNm("우선순위 소통정보").build();
        LinkCollectSetupDto prioEtc = LinkCollectSetupDto.builder().clctSystCd(PRIOETC).clctSystNm("우선순위(ETC) 소통정보").build();
        LinkCollectSetupDto moct = LinkCollectSetupDto.builder().clctSystCd(MOCT).clctSystNm("국토부민간(MOCT) 소통정보").build();

        oper.init();
        prio.init();
        prioEtc.init();
        moct.init();

        this.clctStupMap.put(oper.getClctSystCd(), oper);
        this.clctStupMap.put(prio.getClctSystCd(), prio);
        this.clctStupMap.put(prioEtc.getClctSystCd(), prioEtc);
        this.clctStupMap.put(moct.getClctSystCd(), moct);

        log.info("[LinkTrafCollectService] init: {}", this.clctStupMap);
    }

    public LinkCollectResultDto get(String key) {
        return this.mapData.get(key);
    }

    public void initialize() {
        // 수집원 별 수집 데이터 초기화
        for (String id: this.mapData.keySet()) {
            this.mapData.get(id).getLists().clear();
        }
        this.mapData.clear();
        this.mapData = new ConcurrentHashMap<>();

        // 수집원 별 수집 건수 초기화
        this.clctStupMap.forEach((key, value) -> {
            value.init();
        });
    }

    @ProcessingElapsed(type="COLLECT", name="수집원별 교통정보 조회", starting = false, endLine = true)
    @Override
    public boolean processing() {
//        initialize(); // 작업전에 초기화 하지 않고 작업이 끝난후에 초기화한다.(LinkTrafPrcsPrepareService)

        int clctSystCnt = this.clctStupMap.size();
        if (clctSystCnt == 0) {
            log.error("[ERR] LinkTrafCollectService.processing: There is no collection system.");
            return false;
        }

        LinkCollectParam collectParam = LinkCollectParam.builder()
                .prcsTime(ApplicationRepository.trafPrcsTime.getPrcsTime())
                .build();

        ExecutorService executorService = Executors.newFixedThreadPool(clctSystCnt);
        for (String clctSystCode: this.clctStupMap.keySet()) {
            LinkCollectSetupDto clctStup = this.clctStupMap.get(clctSystCode);
            executorService.execute(new LinkTrafCollectWorker(clctStup, collectParam, this.mapData));
        }

        try {
            executorService.shutdown();
            if (!executorService.awaitTermination(120, TimeUnit.SECONDS)) {
                log.warn("[WRN] LinkTrafCollectService.processing: LinkTrafCollectWorker timeout, shutting down executor service.");
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            log.error("[ERR] LinkTrafCollectService.processing: LinkTrafCollectWorker InterruptedException, {}", e.getMessage());
            executorService.shutdownNow();
        }

        // 수집원 별 수집 데이터 건수 설정
        int oper, moct, pri, priEtc;
        oper = moct = pri = priEtc = 0;
        for (String clctSystCode: this.clctStupMap.keySet()) {
            LinkCollectSetupDto systStup = this.clctStupMap.get(clctSystCode);
            if (OPER.equals(clctSystCode)) {
                oper = systStup.getClctNum();
            }
            else if (MOCT.equals(clctSystCode)) {
                moct = systStup.getClctNum();
            }
            else if (PRIO.equals(clctSystCode)) {
                pri = systStup.getClctNum();
            }
            else if (PRIOETC.equals(clctSystCode)) {
                priEtc = systStup.getClctNum();
            }

//            LinkCollectResultDto collect = this.mapData.get(clctSystCode);
//            if (collect != null) {
//                systStup.setClctNum(collect.getLists().size());
//
//                if (OPER.equals(collect.getClctSystCd())) {
//                    oper = systStup.getClctNum();
//                }
//                else if (MOCT.equals(collect.getClctSystCd())) {
//                    moct = systStup.getClctNum();
//                    final long tmpStart = System.currentTimeMillis();
//                    collect.getLists().parallelStream()
//                            .forEach(clctDto -> {
//                                LinkClctMoctDto clctMoct = this.moctMap.get(clctDto.getLinkId());
//                                if (clctMoct != null) {
//                                    clctMoct.setSpeed(clctDto.getSpeed());
//                                    clctMoct.setValid(true);
//                                } else
//                                    this.moctMap.put(clctDto.getLinkId(), LinkClctMoctDto.builder()
//                                            .linkId(clctDto.getLinkId())
//                                            .speed(clctDto.getSpeed())
//                                            .valid(true)
//                                            .build());
//                            });
//                    log.info("[INF] {}", LogUtils.elapsedLog("SAVE MOCT MAP", moct, System.currentTimeMillis() - tmpStart));
//                }
//                else if (PRIO.equals(collect.getClctSystCd())) {
//                    pri = systStup.getClctNum();
//                }
//                else if (PRIOETC.equals(collect.getClctSystCd())) {
//                    priEtc = systStup.getClctNum();
//                }
//            }
//            log.info("[INF] {}: {} EA.", LogUtils.elapsedLog(clctSystCode + systStup.getClctSystNm()), systStup.getClctNum());
        }

        if (this.trace.isDebug()) {
            log.info("[INF] {}: OPER({}), PRIO({}), PRIOETC({}), MOCT({})",
                    LogUtils.elapsedLog("수집원별 교통정보"), oper, pri, priEtc, moct);
        }
        return true;
    }

}
