package com.its.op.service.its.facility;

import com.its.op.dao.repository.its.code.TbCmmnCdRepository;
import com.its.op.dao.repository.its.facility.TbFcltFailActRepository;
import com.its.op.dao.repository.its.facility.TbFcltFailRepository;
import com.its.op.dto.its.common.NewIdStringDto;
import com.its.op.dto.its.common.UsageCountDto;
import com.its.op.dto.its.facility.*;
import com.its.op.entity.its.code.TbCmmnCd;
import com.its.op.entity.its.facility.TbFcltFail;
import com.its.op.global.CodeManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
@Service
public class TbFcltFailService {

    private final TbFcltFailRepository repo;
    private final TbFcltFailActRepository actRepo;
    private final TbCmmnCdRepository cmmnCdRepo;

    // 데이터 1건 조회, 없으면 exception
    private TbFcltFail requireOne(String failNo) throws NoSuchElementException {
        Optional<TbFcltFail> info = this.repo.findById(failNo);
        if (info.isPresent()) {
            return info.get();
        }
        else {
            throw new NoSuchElementException("데이터가 존재하지 않습니다: " + failNo);
        }
    }

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

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

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

    // 데이터 변경 또는 생성-개별(데이터가 존재하면 업데이트 없으면 신규로 생성)
    @Transactional
    public TbFcltFailDto mergeInfo(TbFcltFailDto.TbFcltFailUpdReq req) {
        if (req.getStepCd() == null) {
            req.setStepCd("ACT0");
        }
        TbFcltFail obj = req.toEntity();
        this.repo.save(obj);
        return obj.toDto();
    }

    // 정보 삭제-개별, 데이터 존재하지 않으면 Exception
    @Transactional
    public TbFcltFailDto deleteById(String failNo) {
        TbFcltFail entity = requireOne(failNo);
        this.repo.deleteById(failNo);
        return entity.toDto();
    }

    /**
     * 신규 장애 관리 번호 조회
     * @return
     */
    @Transactional(readOnly = true)
    public NewIdStringDto getNewNmbr() {
        String newId = this.repo.getNewNmbr();
        return NewIdStringDto.builder().newId(newId).build();
    }

    /**
     * 장애 진행 단계 코드 목록
     * @return
     */
    @Transactional(readOnly = true)
    public List<FcltFailStepDto> findAllFailStep() {
        List<FcltFailStepDto> result = new ArrayList<>();
        List<TbCmmnCd> data = this.cmmnCdRepo.findAllByCmmnClsfCd(CodeManager.FMS_FSTEP);
        if (data != null) {
            data.forEach(obj -> {
                if (("Y").equals(obj.getUseYn())) {
                    result.add(FcltFailStepDto.makeDto(obj));
                }
            });
        }
        return result;
    }

    /**
     * 장애 등록 단계 코드 목록
     * @return
     */
    @Transactional(readOnly = true)
    public List<FcltFailRegDto> findAllFailReg() {
        List<FcltFailRegDto> result = new ArrayList<>();
        List<TbCmmnCd> data = this.cmmnCdRepo.findAllByCmmnClsfCd(CodeManager.FMS_FREG);
        if (data != null) {
            data.forEach(obj -> {
                if (("Y").equals(obj.getUseYn())) {
                    result.add(FcltFailRegDto.makeDto(obj));
                }
            });
        }
        return result;
    }

    /**
     * 장애 등급 코드 목록
     * @return
     */
    @Transactional(readOnly = true)
    public List<FcltFailLevlDto> findAllFailLevl() {
        List<FcltFailLevlDto> result = new ArrayList<>();
        List<TbCmmnCd> data = this.cmmnCdRepo.findAllByCmmnClsfCd(CodeManager.FMS_FLVL);
        if (data != null) {
            data.forEach(obj -> {
                if (("Y").equals(obj.getUseYn())) {
                    result.add(FcltFailLevlDto.makeDto(obj));
                }
            });
        }
        return result;
    }

    /**
     * 장애 날씨 코드 목록
     * @return
     */
    @Transactional(readOnly = true)
    public List<FcltFailWthrDto> findAllWthr() {
        List<FcltFailWthrDto> result = new ArrayList<>();
        List<TbCmmnCd> data = this.cmmnCdRepo.findAllByCmmnClsfCd(CodeManager.FMS_FWTHR);
        if (data != null) {
            data.forEach(obj -> {
                if (("Y").equals(obj.getUseYn())) {
                    result.add(FcltFailWthrDto.makeDto(obj));
                }
            });
        }
        return result;
    }

    /**
     * 장애 현황 목록 조회
     * @param all : true(모든 데이터 조회), false(조치완료된 항목 제외)
     * @param fromDay
     * @param toDay
     * @return
     */
    @Transactional(readOnly = true)
    public List<TbFcltFailDto> findAllListByDate(boolean all, String fromDay, String toDay) {
        List<TbFcltFailDto> result = new ArrayList<>();
        List<TbFcltFail> data;
        if (all) {
            data = this.repo.findListAll(fromDay + "000000", toDay + "235959");
        }
        else {
            data = this.repo.findListContinuous(fromDay + "000000", toDay + "235959");
        }
        for (TbFcltFail entity : data) {
            // TODO
            result.add(entity.toDto());
        }
        return result;
    }

    public UsageCountDto findUsageId(String failNo) {
        UsageCountDto result = UsageCountDto.builder()
                .count(0)
                .build();
        Integer data = this.actRepo.findUsageCountById(failNo);
        result.setCount(data);
        return result;
    }
}
