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.common.utils.TimeUtils;
import com.utic.center.utic.traf.server.config.TraceConfig;
import com.utic.center.utic.traf.server.dao.repository.dwdb.DwdbParamRepository;
import com.utic.center.utic.traf.server.dao.repository.dwdb.LinkPatternRepository;
import com.utic.center.utic.traf.server.dto.LinkDto;
import com.utic.center.utic.traf.server.dto.LinkPtrnVal;
import com.utic.center.utic.traf.server.dto.LinkTrafState;
import com.utic.center.utic.traf.server.dto.dwdb.ParamHoliday;
import com.utic.center.utic.traf.server.dto.dwdb.ParamPatternEffectDay;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Getter
@RequiredArgsConstructor
@Service
public class LinkPatternService implements AbstractProcessService {

    private final TraceConfig trace;
    private final LinkService linkService;
    private final DwdbParamRepository dwdbParamRepo;
    private final LinkPatternRepository patternRepo;

    private final ParamPatternEffectDay patternEffectDay = ParamPatternEffectDay.builder()
            .effectDays(365)
            .build();

    private final ParamHoliday holiday = ParamHoliday.builder()
            .wDay(0)
            .hCnt(0)
            .build();

    private int patternCount = 0;
    private String currTime = "20000101000000";
    private String prcsTime = "20000101000000";
    private String patternTime = "20000101000000";

    private boolean initialize() {
        this.currTime = TimeUtils.getCurrentTimeString();
        String nextTime = TimeUtils.addMinutes(this.currTime, 5);
        this.prcsTime = TimeUtils.getFiveMinString(TimeUtils.stringToDate(nextTime));
        String patternTime = TimeUtils.roundToQuarterHour(this.prcsTime);

        if (patternTime.equals(this.patternTime)) {
            log.info("[INF] {}: next-prcs({}), pattern({}).",
                    LogUtils.elapsedLog("Pattern Time"), this.prcsTime, this.patternTime);
            return false;
        }
        if (this.trace.isDebug()) {
            log.info("[INF] {}: next-prcs({}), pattern({}), old-pattern({}}.",
                    LogUtils.elapsedLog("Pattern Time"), this.prcsTime, patternTime, this.patternTime);
        }
        this.patternTime = patternTime;
        return true;
    }

    @ProcessingElapsed(type="PATTERN", name="링크 패턴정보 로딩", starting = false, endLine = true)
    @Override
    public boolean processing() {
        if (initialize()) {
            // 패턴정보를 조회해야 할 시간(patternTime)이 변경되었을 때만 패턴정보를 조회한다.
            loadPtrnEffectDays();
            loadPtrnHoliday();

            this.linkService.initLinkPtrn();

            loadPatternSpeed();
        }
        return true;
    }

    public void loadPatternSpeed() {
        final long start = System.currentTimeMillis();
        final String logDate = TimeUtils.addDays(this.patternTime, -this.patternEffectDay.getEffectDays());
        final String ptrnTime = this.patternTime.substring(8, 12);

        String logTable;
        List<LinkPtrnVal> level1 = null;
        List<LinkPtrnVal> level4 = null;
        if (this.holiday.getHCnt() > 0) {
            logTable = "(휴일)PATN_15MIN_HOLIDAY";
            level1 = this.patternRepo.findLinkPatternHoliday15M(ptrnTime, logDate, "1");
            level4 = this.patternRepo.findLinkPatternHoliday15M(ptrnTime, logDate, "4");
        }
        else {
            switch(this.holiday.getWDay()) {
                case 1: // 일요일 패턴
                    logTable = "(일요일)PATN_15MIN_SUN";
                    level1 = this.patternRepo.findLinkPatternSun15M(ptrnTime, logDate, "1");
                    level4 = this.patternRepo.findLinkPatternSun15M(ptrnTime, logDate, "4");
                    break;
                case 2: // 월요일 패턴
                    logTable = "(월요일)PATN_15MIN_MON";
                    level1 = this.patternRepo.findLinkPatternMon15M(ptrnTime, logDate, "1");
                    level4 = this.patternRepo.findLinkPatternMon15M(ptrnTime, logDate, "4");
                    break;
                case 6: // 금요일 패턴
                    logTable = "(금요일)PATN_15MIN_FRI";
                    level1 = this.patternRepo.findLinkPatternFri15M(ptrnTime, logDate, "1");
                    level4 = this.patternRepo.findLinkPatternFri15M(ptrnTime, logDate, "4");
                    break;
                case 7: // 토요일 패턴
                    logTable = "(토요일)PATN_15MIN_SAT";
                    level1 = this.patternRepo.findLinkPatternSat15M(ptrnTime, logDate, "1");
                    level4 = this.patternRepo.findLinkPatternSat15M(ptrnTime, logDate, "4");
                    break;
                default: // 평일 패턴(3,4,5)
                    logTable = "(평일)PATN_15MIN_WEEK";
                    level1 = this.patternRepo.findLinkPatternWeek15M(ptrnTime, logDate, "1");
                    level4 = this.patternRepo.findLinkPatternWeek15M(ptrnTime, logDate, "4");
                    break;
            }
        }
        int level1Count = setLinkPatternSpeed(level1);
        int level4Count = setLinkPatternSpeed(level4);
        this.patternCount = level1Count + level4Count;
        log.info("[INF] {}, [레벨1: {}, 레벨4: {}], ({}), ({})",
                LogUtils.elapsedLog(logTable, System.currentTimeMillis() - start),
                level1Count, level4Count, ptrnTime, logDate);
    }

    private int setLinkPatternSpeed(List<LinkPtrnVal> result) {
        // 링크레벨1, 레벨4 패턴 속도 설정(테이블에 레벨1, 레벨4만 존재)
        int linkCount = 0;
        for (LinkPtrnVal ptrnVal : result) {
            LinkDto link = this.linkService.getLink(ptrnVal.getLinkId());
            if (link == null) {
                log.error("[ERR] LinkPatternService.setLinkPatternSpeed: {} link not found.", ptrnVal.getLinkId());
                continue;
            }
            link.getTrafPtrn().getState().setState(true);
            link.getTrafPtrn().getState().setCrtDataType(LinkTrafState.TRAFFIC_CRT_DATA_TYPE.CRT_DATA_NORMAL);
            link.getTrafPtrn().setSpeed(ptrnVal.getSpeed());
            linkCount++;
        }
        return linkCount;
    }

    public void loadPtrnEffectDays() {
        ParamPatternEffectDay effectDays = this.dwdbParamRepo.findPtrnEffectDays();
        if (effectDays == null) {
            return;
        }
        this.patternEffectDay.setParam(effectDays);
        if (this.trace.isDebug()) {
            log.info("[INF] {}: 패턴유효일({})",
                    LogUtils.elapsedLog("패턴 결측파라미터"), this.patternEffectDay.getEffectDays());
        }
    }

    public void loadPtrnHoliday() {
        ParamHoliday holiday = this.dwdbParamRepo.findHoliday(this.patternTime);
        if (holiday == null) {
            this.holiday.setParam(ParamHoliday.builder().hCnt(0).wDay(0).build());
            return;
        }
        this.holiday.setParam(holiday);
        if (this.trace.isDebug()) {
            log.info("[INF] {}: [휴일유무({}), 요일({})]",
                    LogUtils.elapsedLog("패턴 휴일"), this.holiday.getHCnt(), this.holiday.getWDay());
        }
    }

}
