package com.its.api.bis.service;

import com.its.api.bis.model.dto.BitDto;
import com.its.api.bis.model.entity.Bit;
import com.its.api.bis.model.entity.BitStatusHistory;
import com.its.api.bis.repository.BitRepository;
import com.its.api.bis.repository.BitStatusHistoryRepository;
import com.its.api.its.global.CodeManager;
import com.its.api.its.model.dto.common.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@RequiredArgsConstructor
@Service
public class BitService {

    private final BitRepository repo;
    private final BitStatusHistoryRepository sttsHsRepo;

    // 데이터 1건 조회, 없으면 exception
    private Bit requireOne(String id) {
        return this.repo.findById(id)
                .orElseThrow(() -> new NoSuchElementException("데이터가 존재하지 않습니다: " + id));
    }

    // 전체 데이터 조회
    @Transactional(readOnly = true)
    public List<BitDto> findAll() {
        List<BitDto> result = new ArrayList<>();
        List<Bit> data = this.repo.findAll();
        for (Bit entity : data) {
            result.add(entity.toDto());
        }
        return result;
    }

    // 데이터 1건 조회(기존 데이터가 반드시 존재해야 함)
    @Transactional(readOnly = true)
    public BitDto findById(String id) {
        Bit entity = requireOne(id);
        return entity.toDto();
    }

    /**
     * BIT 목록 조회
     * @return
     */
    public List<BitDto> findAllList() {
        List<BitDto> result = new ArrayList<>();
        List<Bit> data = this.repo.findAllList();
        data.forEach(obj -> {
            result.add(obj.toDto());
        });
        return result;
    }

    /**
     * RSE 상태 통합
     * @return
     */
    @Transactional(readOnly = true)
    public FcltSttsSummaryDto findAllSttsSummary() {

        Map<String, FcltSttsDto> resultMap = new HashMap<>();
        List<Bit> ctlrList = this.repo.findAllList();
        LocalDateTime fromDt = LocalDate.now().atTime(0, 0, 0);
        List<BitStatusHistory> sttsList = this.sttsHsRepo.findAllStts(fromDt);
        int total = ctlrList.size();
        AtomicInteger normal = new AtomicInteger();
        // 전체 BIT 상태정보 초기화
        ctlrList.forEach(obj -> {
            FcltSttsDto stts = FcltSttsDto.builder()
                    .fcltType("BIT")
                    .fcltNmbr(String.valueOf(obj.getBitid()))
                    .fcltId(obj.getBitid())
                    .fcltNm(obj.getBitname())
                    .fcltKind(obj.getBittype())
                    .xCrdn(obj.getCoordx())
                    .yCrdn(obj.getCoordy())
                    .strmUrl("")
                    .sttsCd(CodeManager.CMNC_STTS_ERROR)
                    .sttsDesc(CodeManager.getCodeDescShort(CodeManager.CMNC_STTS_CD, CodeManager.CMNC_STTS_ERROR))
                    .updtDt("19700101000000")
                    .build();
            resultMap.put(stts.getFcltNmbr(), stts);
        });

        // 통신 상태 조회한다.
        sttsList.forEach(obj -> {
            FcltSttsDto stts = resultMap.get(obj.getBitid());
            if (stts == null) {
                return;
            }
            if (("1").equals(obj.getWcommstatus())) {
                // 유선통신 정상
                normal.getAndIncrement();
            }
        });

        int error = total - normal.get();
        FcltSttsSummaryDto dto = FcltSttsSummaryDto.builder()
                .fcltType("BIT")
                .fcltNm("BIT")
                .totalCnt(total)
                .normalCnt(normal.get())
                .errorCnt(error)
                .colErrCnt(0)
                .build();
        dto.init();
        return dto;
    }

    /**
     * 통신이상 시설물 정보 조회
     * @return
     */
    @Transactional(readOnly = true)
    public List<SttsCommErrDto> findAllListCommError() {

        List<SttsCommErrDto> result = new ArrayList<>();
        Map<String, SttsCommErrDto> resultMap = new HashMap<>();

        LocalDateTime fromDt = LocalDate.now().atTime(0, 0, 0);
        log.error("{}", fromDt);
        List<Bit> ctlrList = this.repo.findAllList();
        List<BitStatusHistory> sttsList = this.sttsHsRepo.findAllStts(fromDt);
        List<SttsHsErrCntInf> errList = this.sttsHsRepo.findAllCommErrorCount(fromDt);

        // 전체 BIT 상태정보 초기화
        ctlrList.forEach(obj -> {
            SttsCommErrDto commErrorDto = new SttsCommErrDto();
            commErrorDto.setCtlrNmbr(obj.getBitid());
            commErrorDto.setCtlrId(obj.getBitid());
            commErrorDto.setCtlrNm(obj.getBitname());
            commErrorDto.setUpdtDt(CodeManager.CMNC_STTS_ERROR_DT);
            commErrorDto.setCommCd(CodeManager.CMNC_STTS_ERROR);
            commErrorDto.setCommDesc(CodeManager.getCodeDescShort(CodeManager.CMNC_STTS_CD, commErrorDto.getCommCd()));
            commErrorDto.setCommErrCnt(0);
            commErrorDto.setColCnt(0);
            resultMap.put(obj.getBitid(), commErrorDto);
        });

        // 통신 상태가 오류 인 제어기 정보만 조회한다.
        sttsList.forEach(obj -> {
            SttsCommErrDto commErrorDto = resultMap.get(obj.getBitid());
            if (commErrorDto == null) {
                return;
            }
            if (("1").equals(obj.getWcommstatus())) {
                // 유선통신 정상
                commErrorDto.setCommCd(CodeManager.CMNC_STTS_NORMAL);
                commErrorDto.setCommDesc(CodeManager.getCodeDescShort(CodeManager.CMNC_STTS_CD, commErrorDto.getCommCd()));
            }
            else {
                // 유선통신 오류
            }
        });

        // 오늘 00:00:00 이후 발생한 오류 건수를 업데이트
        errList.forEach(obj -> {
            SttsCommErrDto dto = resultMap.get(obj.getNmbr());
            if (dto != null) {
                dto.setCommErrCnt(obj.getErrCnt().intValue());
            }
        });

        resultMap.forEach((key, obj) -> {
            if (CodeManager.CMNC_STTS_ERROR.equals(obj.getCommCd())) {
                result.add(obj);
            }
        });

        return result;
    }

    /**
     * 상태 목록
     * @param
     */
    @Transactional(readOnly = true)
    public FcltSttsListDto findAllListSttsTotal() {

        String fcltType = "BIT";
        FcltSttsListDto result = new FcltSttsListDto();
        Map<String, FcltSttsDto> resultMap = new HashMap<>();

        AtomicInteger error = new AtomicInteger(0);
        AtomicInteger normal = new AtomicInteger(0);
        AtomicInteger doorOpen = new AtomicInteger(0);
        AtomicInteger fanRun = new AtomicInteger(0);
        AtomicInteger hetrRun = new AtomicInteger(0);
        AtomicInteger modlErr = new AtomicInteger(0);

        List<FcltSttsDto> sttsDtoList = new ArrayList<>();
        List<Bit> ctlrList = this.repo.findAllList();
        LocalDateTime fromDt = LocalDate.now().atTime(0, 0, 0);
        log.error("{}", fromDt);
        List<BitStatusHistory> sttsList = this.sttsHsRepo.findAllStts(fromDt);

        // 전체 BIT 상태정보 초기화
        ctlrList.forEach(obj -> {
            FcltSttsDto stts = FcltSttsDto.builder()
                    .fcltType(fcltType)
                    .fcltNmbr(String.valueOf(obj.getBitid()))
                    .fcltId(obj.getBitid())
                    .fcltNm(obj.getBitname())
                    .fcltKind(obj.getBittype())
                    .xCrdn(obj.getCoordx())
                    .yCrdn(obj.getCoordy())
                    .strmUrl("")
                    .sttsCd(CodeManager.CMNC_STTS_ERROR)
                    .sttsDesc(CodeManager.getCodeDescShort(CodeManager.CMNC_STTS_CD, CodeManager.CMNC_STTS_ERROR))
                    .updtDt("19700101000000")
                    .build();
            resultMap.put(stts.getFcltNmbr(), stts);
        });

        sttsList.forEach(obj -> {
            FcltSttsDto stts = resultMap.get(obj.getBitid());
            if (stts == null) {
                return;
            }

            if (obj.getRecorddate() != null) {
                String collDt = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(obj.getRecorddate());
                stts.setUpdtDt(collDt);
            }
            if (("1").equals(obj.getWcommstatus())) {
                // 유선통신 정상
                normal.getAndIncrement();
                stts.setSttsCd(CodeManager.CMNC_STTS_NORMAL);
                stts.setSttsDesc(CodeManager.getCodeDescShort(CodeManager.CMNC_STTS_CD, stts.getSttsCd()));
                if (("0").equals(obj.getDoorstatus())) {
                    doorOpen.getAndIncrement();
                }
                if (("0").equals(obj.getFanstatus())) {
                    fanRun.getAndIncrement();
                }
                if (("0").equals(obj.getHeateropstatus())) {
                    hetrRun.getAndIncrement();
                }
                if (("0").equals(obj.getMonitorstatus())) {
                    // BIT 모니터 상태
                    modlErr.getAndIncrement();
                }
            }
        });

        resultMap.forEach((key, obj) -> {
            sttsDtoList.add(obj);
        });

        FcltSttsSummaryDto summaryDto = FcltSttsSummaryDto.builder()
                .fcltType(fcltType)
                .fcltNm(fcltType)
                .totalCnt(ctlrList.size())
                .normalCnt(normal.get())
                .errorCnt(error.get())
                .colErrCnt(0)
                .build();

        summaryDto.init();
        summaryDto.setDoorOpen(doorOpen.get());
        summaryDto.setFanRun(fanRun.get());
        summaryDto.setHetrRun(hetrRun.get());
        summaryDto.setModlErr(modlErr.get());
        summaryDto.setFcltObjs(sttsDtoList);

        result.getFcltList().add(summaryDto);

        return result;
    }

    /**
     * 상태 목록
     * @param
     */
    @Transactional(readOnly = true)
    public List<FcltSttsDto> findAllListCommStts() {
        FcltSttsListDto result = findAllListSttsTotal();
        return result.getFcltList().get(0).getFcltObjs();
    }
}
