package com.utic.center.utic.ptis.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.ptis.server.dao.repository.UticPtisServerRepository;
import com.utic.center.utic.ptis.server.dto.IndigoStatsDataDto;
import com.utic.center.utic.ptis.server.repository.ApplicationRepository;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.List;

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

    private final UticPtisServerRepository repo;

    private final String INCD_ID = "IF_POL_MPSS_001";
    private final String EVNT_ID = "IF_POL_MPSS_002";
    private final String TRAF_ID = "IF_POL_MPSS_003";
    private final String INCD_INFO = "돌발정보";
    private final String EVNT_INFO = "통제정보";
    private final String TRAF_INFO = "소통정보";
    private final String agency = "도로교통공단";
    private final String period = "5분";

    private int traf;
    private int evnt;
    private int incd;

    @PreDestroy
    void destroy() {
        log.info("UticPtisServerService.destroy: start.");
        // 프로그램 종료시 통계정보를 최종적으로 저장(내일 날짜로 저장됨)
//        this.repo.mergeIndigoStats(this.INCD_ID, this.agency, this.INCD_INFO, this.period, String.valueOf(ApplicationRepository.indigoStats.getIncdCnt()));
//        this.repo.mergeIndigoStats(this.EVNT_ID, this.agency, this.EVNT_INFO, this.period, String.valueOf(ApplicationRepository.indigoStats.getEvntCnt()));
//        this.repo.mergeIndigoStats(this.TRAF_ID, this.agency, this.TRAF_INFO, this.period, String.valueOf(ApplicationRepository.indigoStats.getTrafCnt()));

        log.info("UticPtisServerService.destroy: end.");
    }

    @ProcessingElapsed(type="INIT", name="Application Initialization")
    public boolean initialize() {
        try {
            String result = this.repo.findLastRegDateCurrLinkSt();
            if (result == null) {
                log.error("[INF] {}: No data found for last registration date(CurrLinkSt).", LogUtils.elapsedLog("initialize.findLastRegDateCurrLinkSt"));
                return false;
            }
            ApplicationRepository.lastRegDate.setTrafDt(result);

            result = this.repo.findLastRegDateEventId();
            if (result == null) {
                log.error("[INF] {}: No data found for last registration date(EventId).", LogUtils.elapsedLog("initialize.findLastRegDateEventId"));
                return false;
            }
            ApplicationRepository.lastRegDate.setEvntDt(result);

            result = this.repo.findLastRegDateOccurId();
            if (result == null) {
                log.error("[INF] {}: No data found for last registration date(OccurId).", LogUtils.elapsedLog("initialize.findLastRegDateOccurId"));
                return false;
            }
            ApplicationRepository.lastRegDate.setIncdDt(result);
            log.info("[INF] {}: Last registration date. {}", LogUtils.elapsedLog("LAST_REG_DATE"), ApplicationRepository.lastRegDate);
            return true;
        } catch (Exception e) {
            log.error("[INF] {}: Exception: {}", LogUtils.elapsedLog("initialize.findLastRegDate"), e.getMessage());
            return false;
        }
    }

    @Override
    public boolean processing() {
        return true;
    }

    public void init() {
        this.traf = 0;
        this.evnt = 0;
        this.incd = 0;
    }
    @ProcessingElapsed(type="INDIGO_STATS", name="Processing(SEL)", starting = false)
    public void initIndigoStats() {

        // 작업 시작시 indigoStats 통계정보를 데이터베이스에서 조회하여 이전 정보를 읽어온다.(내일 날짜로 조회한다)
        ApplicationRepository.indigoStats.reset(); // 통계정보 초기화
        List<IndigoStatsDataDto> indigoStats = this.repo.findAllIndigoStats();
        if (indigoStats != null) {
            for (IndigoStatsDataDto stats : indigoStats) {
                switch (stats.getIfId()) {
                    case INCD_ID:
                        ApplicationRepository.indigoStats.setIncdCnt(stats.getCountSnd());
                        break;
                    case EVNT_ID:
                        ApplicationRepository.indigoStats.setEvntCnt(stats.getCountSnd());
                        break;
                    case TRAF_ID:
                        ApplicationRepository.indigoStats.setTrafCnt(stats.getCountSnd());
                        break;
                    default:
                        log.warn("[INF] {}: Unknown IF_ID: {}", LogUtils.elapsedLog("initialize.findAllIndigoStats"), stats.getIfId());
                }
            }
        }
        printIndigoStats("Current");
    }

    private void printIndigoStats(String message) {
        log.info("[INF] {}: {}, TRAF: {}, EVNT: {}, INCD: {}",
                LogUtils.elapsedLog("INDIGO_STATUS"),
                message,
                ApplicationRepository.indigoStats.getTrafCnt(),
                ApplicationRepository.indigoStats.getEvntCnt(),
                ApplicationRepository.indigoStats.getIncdCnt());
    }
    @ProcessingElapsed(type="INDIGO_STATS", name="Processing(INS)", starting = false)
    public void insIndigoStats() {
        // 통계정보를 매 작업주기마다 업데이트 하고 작업전에 조회해서 처리하도록 수정함.
        try {
            this.repo.insertIndigoStats(this.INCD_ID, this.agency, this.INCD_INFO, this.period, String.valueOf(ApplicationRepository.indigoStats.getIncdCnt()));
            this.repo.insertIndigoStats(this.EVNT_ID, this.agency, this.EVNT_INFO, this.period, String.valueOf(ApplicationRepository.indigoStats.getEvntCnt()));
            this.repo.insertIndigoStats(this.TRAF_ID, this.agency, this.TRAF_INFO, this.period, String.valueOf(ApplicationRepository.indigoStats.getTrafCnt()));
//            printIndigoStats();
        } catch (Exception e) {
            log.error("[INF] {}: Exception: {}", LogUtils.elapsedLog("insertIndigoStats"), e.getMessage());
        }
   }

    private void printResult(String message, int result) {
        log.info("[INF] {}: {} EA.", LogUtils.elapsedLog(message), result);
    }

    @ProcessingElapsed(type="CURLINKST_POLICE_LOG", name="Processing", starting = false)
    public void jobEtlpTraf() {
        int result = 0;
        String regDate;
        final String fromDate = ApplicationRepository.lastRegDate.getTrafDt();
        final String trafTable = "TRAF_SYSDATE";

        // 교통정보 현재시간 조회
        regDate = this.repo.findOneSysNow();
        log.info("[INF] {}: TRAF: OLD({}), NEW({})", LogUtils.elapsedLog(trafTable), fromDate, regDate);
        if (regDate == null || regDate.isEmpty()) {
            log.warn("[INF] {}: No traf time data found.", LogUtils.elapsedLog(trafTable));
            regDate = TimeUtils.getCurrentTimeString(); // 현재시간으로 갱신
        }
        if (regDate.equals(fromDate)) {
            log.info("[INF] {}: No new traf data to process.", LogUtils.elapsedLog(trafTable));
            result = 0; // 새로운 교통정보가 없음
        }
        else {
            // 교통정보 이관
            result = this.repo.insertCurlinkstPoliceLog();
            if (result < 0) {
                log.error("[INF] {}: Failed to insert traffic data.", LogUtils.elapsedLog("CURLINKST_POLICE_LOG"));
                result = 0; // 교통정보 이관 실패
            }
        }

        this.traf = result; // 교통정보 처리 건수 저장
        ApplicationRepository.lastRegDate.setTrafDt(regDate); // 교통정보 최종시각 갱신
        ApplicationRepository.indigoStats.setTrafCnt(result + ApplicationRepository.indigoStats.getTrafCnt());
//        printResult("Total Traffic", ApplicationRepository.indigoStats.getTrafCnt());
    }

    @ProcessingElapsed(type="EVENTID_POLICE_LOG", name="Processing", starting = false)
    public void jobEtlpEvnt() {
        int result = 0;
        String regDate;
        final String fromDate = ApplicationRepository.lastRegDate.getEvntDt();
        final String imsDataTable = "UTISINCI.TB_IMS_DATA@UTISDB";

        // 이벤트 최종시각 조회
        regDate = this.repo.findOneMaxEventRegDate();
        log.info("[INF] {}: EVNT: OLD({}), NEW({})", LogUtils.elapsedLog(imsDataTable), fromDate, regDate);
        if (regDate == null || regDate.isEmpty()) {
            log.warn("[WAN] {}: No evnt time data found.", LogUtils.elapsedLog(imsDataTable));
            regDate = TimeUtils.getCurrentTimeString(); // 현재시간으로 갱신
        }
        if (regDate.equals(fromDate)) {
            log.info("[INF] {}: No new evnt data to process.", LogUtils.elapsedLog(imsDataTable));
            result = 0; // 새로운 통제정보가 없음
        }
        else {
            // 통제정보 이관
            result = this.repo.insertEventidPoliceLog(fromDate, regDate);
            if (result < 0) {
                log.error("[INF] {}: Failed to insert event data.", LogUtils.elapsedLog("EVENTID_POLICE_LOG"));
                result = 0; // 통제정보 이관 실패
            }

            // 임시 통제정보테이블에 데이터 이관
            int tmpResult = this.repo.insertEventidPoliceLogTmp(fromDate, regDate);
            if (tmpResult < 0) {
                log.error("[INF] {}: Failed to insert temporary event data.", LogUtils.elapsedLog("EVENTID_POLICE_LOG_TMP"));
            }
        }

        this.evnt = result; // 통제정보 처리 건수 저장
        ApplicationRepository.lastRegDate.setEvntDt(regDate); // 통제정보 최종시각 갱신
        ApplicationRepository.indigoStats.setEvntCnt(result + ApplicationRepository.indigoStats.getEvntCnt());
//        printResult("Total Event", ApplicationRepository.indigoStats.getEvntCnt());
    }

    @ProcessingElapsed(type="OCCURID_POLICE_LOG", name="Processing", starting = false)
    public void jobEtlpIncd() {
        int result = 0;
        String regDate;
        final String fromDate = ApplicationRepository.lastRegDate.getIncdDt();
        final String imsDataTable = "UTISINCI.TB_IMS_DATA@UTISDB";

        // 이벤트 최종시각 조회
        regDate = this.repo.findOneMaxIncdRegDate();
        log.info("[INF] {}: INCD: OLD({}), NEW({})", LogUtils.elapsedLog(imsDataTable), fromDate, regDate);
        if (regDate == null || regDate.isEmpty()) {
            log.warn("[WAN] {}: No incd time data found.", LogUtils.elapsedLog(imsDataTable));
            regDate = TimeUtils.getCurrentTimeString(); // 현재시간으로 갱신
        }
        if (regDate.equals(fromDate)) {
            log.info("[INF] {}: No new incd data to process.", LogUtils.elapsedLog(imsDataTable));
            result = 0; // 새로운 돌발정보가 없음
        }
        else {
            // 돌발정보 이관
            result = this.repo.insertOccuridPoliceLog(fromDate, regDate);
            if (result < 0) {
                log.error("[INF] {}: Failed to insert incident data.", LogUtils.elapsedLog("OCCURID_POLICE_LOG"));
                result = 0; // 돌발정보 이관 실패
            }
            // 임시 돌발정보테이블에 데이터 이관
            int tmpResult = this.repo.insertOccuridPoliceLogTmp(fromDate, regDate);
            if (tmpResult < 0) {
                log.error("[INF] {}: Failed to insert temporary incident data.", LogUtils.elapsedLog("OCCURID_POLICE_LOG_TMP"));
            }
        }

        this.incd = result; // 돌발정보 처리 건수 저장
        ApplicationRepository.lastRegDate.setIncdDt(regDate); // 돌발정보 최종시각 갱신
        ApplicationRepository.indigoStats.setIncdCnt(result + ApplicationRepository.indigoStats.getIncdCnt());
//        printResult("Total Incident", ApplicationRepository.indigoStats.getIncdCnt());
    }

    @ProcessingElapsed(type="BATCH", name="DELETE Processing", starting = false)
    public void jobDeleteTable() {
        int result = 0;

        // 수신 교통정보 삭제
        result = this.repo.deleteRcvLinkTraffic();
        if (result < 0) {
            log.error("[INF] {}: Failed to delete rcv traffic data.", LogUtils.elapsedLog("RCV_LINK_TRAFFIC"));
        }

        // 통제정보 삭제
        result = this.repo.deleteEventidPoliceLog();
        if (result < 0) {
            log.error("[INF] {}: Failed to delete event data.", LogUtils.elapsedLog("EVENTID_POLICE_LOG"));
        }
        // 임시 통제정보테이블 삭제
        result = this.repo.deleteEventidPoliceLogTmp();
        if (result < 0) {
            log.error("[INF] {}: Failed to delete temporary event data.", LogUtils.elapsedLog("EVENTID_POLICE_LOG_TMP"));
        }

        // 돌발정보 삭제
        result = this.repo.deleteOccuridPoliceLog();
        if (result < 0) {
            log.error("[INF] {}: Failed to delete incident data.", LogUtils.elapsedLog("OCCURID_POLICE_LOG"));
        }
        // 임시 돌발정보테이블 삭제
        result = this.repo.deleteOccuridPoliceLogTmp();
        if (result < 0) {
            log.error("[INF] {}: Failed to delete temporary incident data.", LogUtils.elapsedLog("OCCURID_POLICE_LOG_TMP"));
        }
    }

}


//    private final SqlSessionFactory sqlSessionFactory;
//    private SqlSession session;


//    private void logProcess(String processName, Elapsed elapsed) {
//        Consumer<String> logElapsed = process ->
//                log.info("[INF] ..... {}", LogUtils.elapsedLog(process + " 종료", elapsed.milliSeconds()));
//        logElapsed.accept(processName);
//    }


//    private boolean openSession() {
//        try {
//            this.session = this.sqlSessionFactory.openSession();
//            this.mapper = this.session.getMapper(PtisJobPrcsMapper.class);
//            return true;
//        } catch (Exception e) {
//            log.error("[INF] (OPEN) {}: Exception: {}", LogUtils.elapsedLog("세션열기"), e.getMessage());
//            return false;
//        }
//        return true;
//    }
//    private boolean closeSession() {
//        try {
//            if (this.session != null) {
//                this.session.close();
//                this.session = null;
//                this.mapper = null;
//            }
//            return true;
//        } catch (Exception e) {
//            log.error("[INF] (CLOSE) {}: Exception: {}", LogUtils.elapsedLog("closeSession"), e.getMessage());
//            return false;
//        }
//        return true;
//    }
