package com.its.pis.service;

import com.its.app.AppUtils;
import com.its.app.utils.SysUtils;
import com.its.pis.dao.mapper.PisInfrMapper;
import com.its.pis.dao.mapper.PrltCtlrMapper;
import com.its.pis.domain.NET;
import com.its.pis.entity.*;
import com.its.pis.global.AppRepository;
import com.its.pis.process.DbmsJobData;
import com.its.pis.process.DbmsJobProcess;
import com.its.pis.process.DbmsJobType;
import com.its.pis.websocket.PisConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;

@Slf4j
@Service
@Transactional(rollbackFor = {Exception.class})
public class PisInfrService {

    private PisInfrMapper pisInfrMapper;
    private PrltCtlrMapper prltCtlrMapper;

    private int sttsMin;

    @PostConstruct
    private void init() {
        log.info("PisInfrService.init: Start.");
        this.pisInfrMapper = (PisInfrMapper) AppUtils.getBean(PisInfrMapper.class);
        this.prltCtlrMapper = (PrltCtlrMapper) AppUtils.getBean(PrltCtlrMapper.class);
        log.info("PisInfrService.init: ..End.");
    }

    @PreDestroy
    public void destroyService() {
        log.error("UnitSystService.destroy. system terminated.......");
        updateCtlrStts(false);
    }

    /**
     * 기초정보 메모리 로딩
     */
    public void loadDb() {
        loadPisInfr();
        loadPisInfrStts();
        loadPisRltnPrkPlce();
    }

    /**
     * 주차정보시스템 정보조회
     */
    public void loadPisInfr() {
        List<TbPisInfr> objList = this.pisInfrMapper.selectAll();
        for (TbPisInfr obj : objList) {
            obj.setPIS_IP(obj.getPIS_IP().trim());
            obj.setPIS_CYCLE(Math.max(5, obj.getPIS_CYCLE()));
            obj.getStts().setPIS_NMBR(obj.getPIS_NMBR());
            obj.getStts().initStts(false);

            AppRepository.getInstance().getPisInfrMap().put(obj.getPIS_NMBR(), obj);
            AppRepository.getInstance().getPisIpAddrMap().put(obj.getPIS_IP(), obj);
            AppRepository.getInstance().getPisTokenMap().put(obj.getPIS_TOKEN(), obj);
            AppRepository.getInstance().getPisIdMap().put(obj.getPIS_ID(), obj);
        }

        log.info("loadPisInfr.PIS NMBR List............... {} EA.", AppRepository.getInstance().getPisInfrMap().size());
        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisInfrMap().entrySet()) {
            log.info("  {}", e.toString());
        }
        log.info("loadPisInfr.PIS IP List............ {} EA.", AppRepository.getInstance().getPisIpAddrMap().size());
        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisIpAddrMap().entrySet()) {
            log.info("  {}", e.toString());
        }
        log.info("loadPisInfr.PIS TOKEN List............ {} EA.", AppRepository.getInstance().getPisTokenMap().size());
        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisTokenMap().entrySet()) {
            log.info("  {}", e.toString());
        }
        log.info("loadPisInfr.PIS ID List............ {} EA.", AppRepository.getInstance().getPisIdMap().size());
        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisIdMap().entrySet()) {
            log.info("  {}", e.toString());
        }
    }

    /**
     * 주차정보시스템 통신상태 조회
     */
    public void loadPisInfrStts() {
        List<TbPisInfrStts> sttsList = this.pisInfrMapper.selectPisInfrStts();
        for (TbPisInfrStts stts : sttsList) {
            TbPisInfr obj = AppRepository.getInstance().getPisInfrMap().get(stts.getPIS_NMBR());
            if (obj == null) {
                log.warn("loadPisInfrStts.PIS Stts Not Found, {}", stts.getPIS_NMBR());
            } else {
                obj.getStts().setCONN_DT(stts.getCONN_DT());
                obj.getStts().setCONN_DT(stts.getCONN_DT());
                obj.getStts().setDIS_CONN_DT(stts.getDIS_CONN_DT());
            }
        }
    }

    /**
     * 주차정보시스템에 속한 주차장 정보 조회
     */
    public void loadPisRltnPrkPlce() {

        loadPrkPlce();
        loadPrkPlceInfo();

        List<TbPisRltnPrlt> rltnList = this.pisInfrMapper.selectPisRltnPrkPlce();
        for (TbPisRltnPrlt rltn : rltnList) {
            TbPisInfr obj = AppRepository.getInstance().getPisInfrMap().get(rltn.getPIS_NMBR());
            if (obj == null) {
                log.error("loadPisRltnPrkPlce.PIS Rltn PIS Not Found, {}", rltn.getPIS_NMBR());
                continue;
            }
            TbPrkPlce prlt =  AppRepository.getInstance().getPrltNmbrMap().get(rltn.getPRK_PLCE_NMBR());
            if (prlt == null) {
                log.error("loadPisRltnPrkPlce.PIS Rltn PRK Not Found, {}", rltn.getPRK_PLCE_NMBR());
                continue;
            }

            obj.getRltnPrltMap().put(rltn.getPRK_PLCE_NMBR(), prlt);
        }

        log.info("loadPisRltnPrlt.PIS Rltn PRLT List.................");
        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisInfrMap().entrySet()) {
            log.info("loadPisRltnPrlt.PIS PRLT Prlt: {}", e.getValue().getPIS_NMBR());
            for (Map.Entry<String, TbPrkPlce> l : e.getValue().getRltnPrltMap().entrySet()) {
                log.info("  {}", l.toString());
            }
        }
    }

    /**
     * 주차장 정보 조회
     */
    public void loadPrkPlce() {

        List<TbPrkPlce> objList = this.prltCtlrMapper.selectAll();
        for (TbPrkPlce obj : objList) {
            obj.getStts().setPRK_PLCE_NMBR(obj.getPRK_PLCE_NMBR());
            obj.getStts().initStts(false);

            AppRepository.getInstance().getPrltNmbrMap().put(obj.getPRK_PLCE_NMBR(), obj);
            AppRepository.getInstance().getPrltIdMap().put(obj.getPRK_PLCE_MANAGE_NO(), obj);
        }

        log.info("loadPrkPlce.PRK NMBR List............... {} EA.", AppRepository.getInstance().getPrltNmbrMap().size());
        for (Map.Entry<String, TbPrkPlce> e : AppRepository.getInstance().getPrltNmbrMap().entrySet()) {
            log.info("  {}", e.toString());
        }

        log.info("loadPrkPlce.PRK ID List............... {} EA.", AppRepository.getInstance().getPrltIdMap().size());
        for (Map.Entry<String, TbPrkPlce> e : AppRepository.getInstance().getPrltIdMap().entrySet()) {
            log.info("  {}", e.toString());
        }
    }

    /**
     * 주차장 시설정보 및 운영정보를 메모리에 로딩해 놓는다.
     */
    public void loadPrkPlceInfo() {

    }

    public void updateCtlrStts(boolean isRun) {
        String keyData = "unit";
        boolean insHs = false;
        Calendar cal = Calendar.getInstance();
        int min = cal.get(Calendar.MINUTE);
        if ((min % 5) == 0) {
            insHs = true;
            this.sttsMin = min;
        }

        // 주차정보시스템 상태정보 업데이트
        List<TbPisInfrStts> pisSttsList = Collections.synchronizedList(new ArrayList<>());
        String UPDT_DT = SysUtils.getSysTime();
        int normal = 0;
        int error  = 0;
        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisInfrMap().entrySet()) {
            TbPisInfr pis = e.getValue();
            pis.getStts().setUPDT_DT(UPDT_DT);
            if (isRun && pis.getNetState() >= NET.LOGIN_REQ && pis.getSession() != null) {
                // 주차정보시스템 통신정상
                normal++;
                pis.getStts().setCMNC_STTS_CD("CMS0");
            }
            else {
                // 주차정보시스템 통신이상
                error++;
                pis.getStts().initStts(false);
                for (Map.Entry<String, TbPrkPlce> l : pis.getRltnPrltMap().entrySet()) {
                    TbPrkPlce prlt = l.getValue();
                    prlt.getStts().initStts(false);
                }
            }

            pisSttsList.add(pis.getStts());
        }

        // 주차정보시스템 상태정보 DB 업데이트
        if (pisSttsList.size() > 0) {
            DbmsJobProcess dbmsJobProcess = (DbmsJobProcess) AppUtils.getBean(DbmsJobProcess.class);
            dbmsJobProcess.add(new DbmsJobData(DbmsJobType.DATA_TYPE_PIS_STTS, insHs, pisSttsList, null));
        }
        log.info("PisInfrService.updateCtlrStts: PIS, total {}, normal {}, error {}", normal + error, normal, error);

        // 주차장 상태정보 업데이트
        List<TbPrkPlceStts> prltSttsList = Collections.synchronizedList(new ArrayList<>());
        int normal_prlt = 0;
        int error_prlt = 0;
        for (Map.Entry<String, TbPrkPlce> e : AppRepository.getInstance().getPrltNmbrMap().entrySet()) {
            TbPrkPlce obj = e.getValue();
            obj.getStts().setUPDT_DT(UPDT_DT);
            if (isRun && obj.getNetState() >= NET.LOGIN_REQ) {
                // 주차장 통신정상
                normal_prlt++;
                obj.getStts().setCMNC_STTS_CD("CMS0");
            }
            else {
                // 주차장 통신이상
                error_prlt++;
                obj.getStts().initStts(false);
            }

            prltSttsList.add(obj.getStts());
        }

        // 주차장 상태정보 DB 업데이트
        if (prltSttsList.size() > 0) {
            DbmsJobProcess dbmsJobProcess = (DbmsJobProcess) AppUtils.getBean(DbmsJobProcess.class);
            dbmsJobProcess.add(new DbmsJobData(DbmsJobType.DATA_TYPE_PRLT_STTS, insHs, prltSttsList, null));
        }

        log.info("PisInfrService.updateCtlrStts: PRLT total {}, normal {}, error {}", normal_prlt + error_prlt, normal_prlt, error_prlt);
    }

    /**
     * C2C-S 로 설정되어 있는 주차정보 시스템을 찾아 해당 주차정보 시스템으로 연결하여 통신하는 C2C_Client 핸들러를 실행시킨다.
     */
    public void startC2C_Client() {
        for (Map.Entry<String, TbPisInfr> e : AppRepository.getInstance().getPisInfrMap().entrySet()) {
            TbPisInfr obj = e.getValue();
            if (PisConstants.C2CServer.equals(obj.getPIS_TP())) {
                log.info("Connect to C2C Server: {}", obj);
                obj.createWebsocketClient();
            }
        }
    }

}
