package com.its.api.its.service.vms;

import com.its.api.its.model.dto.vms.TbVmsMsgDsplSchDto;
import com.its.api.its.model.dto.vms.TbVmsMsgDsplSchVmsListDto;
import com.its.api.its.model.entity.vms.TbVmsCtlr;
import com.its.api.its.model.entity.vms.TbVmsMsgDsplSch;
import com.its.api.its.model.entity.vms.TbVmsMsgDsplSchKey;
import com.its.api.its.repository.vms.TbVmsCtlrRepository;
import com.its.api.its.repository.vms.TbVmsMsgDsplSchRepository;
import com.its.api.utils.ItsUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Slf4j
@RequiredArgsConstructor
@Service
public class TbVmsMsgDsplSchService {

    private final TbVmsMsgDsplSchRepository repo;
    private final TbVmsCtlrRepository vmsRepo;

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

    // 전체 데이터 조회
    @Transactional(readOnly = true)
    public List<TbVmsMsgDsplSchDto> findAll() {
        List<TbVmsMsgDsplSchDto> result = new ArrayList<>();
        List<TbVmsMsgDsplSch> data = this.repo.findAll();
        data.forEach(obj -> {
            if (!("5").equals(obj.getTrfcSituTypeCd()) &&
                !("6").equals(obj.getTrfcSituTypeCd())) {
                result.add(obj.toDto());
            }
        });
        return result;
    }

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

    // 데이터 변경
    @Transactional
    public TbVmsMsgDsplSchDto updateById(TbVmsMsgDsplSchKey id, TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq req) {
        TbVmsMsgDsplSch entity = requireOne(id);
        entity.updateInfo(req);
        this.repo.save(entity);
        return entity.toDto();
    }

    // 데이터 변경 또는 생성-개별(데이터가 존재하면 업데이트 없으면 신규로 생성)
    @Transactional
    public TbVmsMsgDsplSchDto mergeInfo(TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq req) {
        TbVmsMsgDsplSch obj = req.toEntity();
        this.repo.save(obj);
        return obj.toDto();
    }

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

    /**
     * VMS 폼 표출 스케줄 조회
     * @param id
     * @param trfcSituTypeCd
     * @return
     */
    public List<TbVmsMsgDsplSchDto> findAllVmsDsplSchedule(Long id, List<String> trfcSituTypeCd) {
        List<TbVmsMsgDsplSchDto> result = new ArrayList<>();
        List<TbVmsMsgDsplSch> data = this.repo.findAllByVmsCtrlTrfcSituTypeCd(id, trfcSituTypeCd);
        data.forEach(obj -> {
            result.add(obj.toDto());
        });
        return result;
    }

    /**
     * 자동 스케줄 목록 조회
     * @param id
     * @return
     */
    public List<TbVmsMsgDsplSchDto> findAllVmsDsplSchedule(Long id) {
        List<String> trfcSituTypeCd = Arrays.asList("0", "1", "2", "3", "4", "7", "8");
        return findAllVmsDsplSchedule(id, trfcSituTypeCd);
    }
    // 교통정보
    public List<TbVmsMsgDsplSchDto> findAllTraffic(Long id) {
        List<String> formTypeCd = Collections.singletonList("0");
        return findAllVmsDsplSchedule(id, formTypeCd);
    }
    // 정체
    public List<TbVmsMsgDsplSchDto> findAllCongest(Long id) {
        List<String> formTypeCd = Collections.singletonList("7");
        return findAllVmsDsplSchedule(id, formTypeCd);
    }
    // 돌발
    public List<TbVmsMsgDsplSchDto> findAllIncident(Long id) {
        List<String> formTypeCd = Collections.singletonList("1");
        return findAllVmsDsplSchedule(id, formTypeCd);
    }
    // 공사/행사
    public List<TbVmsMsgDsplSchDto> findAllEvent(Long id) {
        List<String> formTypeCd = Collections.singletonList("2");
        return findAllVmsDsplSchedule(id, formTypeCd);
    }
    // 홍보
    public List<TbVmsMsgDsplSchDto> findAllHongbo(Long id) {
        List<String> formTypeCd = Collections.singletonList("3");
        return findAllVmsDsplSchedule(id, formTypeCd);
    }
    // 우회도로
    public List<TbVmsMsgDsplSchDto> findAllDetour(Long id) {
        List<String> formTypeCd = Collections.singletonList("4");
        return findAllVmsDsplSchedule(id, formTypeCd);
    }
    // 재난안전
    public List<TbVmsMsgDsplSchDto> findAllSafety(Long id) {
        List<String> formTypeCd = Collections.singletonList("8");
        return findAllVmsDsplSchedule(id, formTypeCd);
    }

    /**
     * 기본 스케줄 목록 조회
     * @param id
     * @return
     */
    public List<TbVmsMsgDsplSchDto> findAllDefault(Long id) {
        List<String> trfcSituTypeCd = Collections.singletonList("6");
        return findAllVmsDsplSchedule(id, trfcSituTypeCd);
    }

    /**
     * 고정 스케줄 목록 조회(홍보폼)
     * @param id
     * @return
     */
    public List<TbVmsMsgDsplSchDto> findAllFixed(Long id) {
        List<String> trfcSituTypeCd = Collections.singletonList("5");
        return findAllVmsDsplSchedule(id, trfcSituTypeCd);
    }

    /**
     * VMS 폼 표출 스케줄 일괄 등록(전체 VMS 편집 - 저장)
     * 기존 데이터 삭제(DELETE)
     * 요청 데이터 저장(INSERT)
     * @param id
     * @param reqList
     * @return
     */
    @Transactional
    public List<TbVmsMsgDsplSchDto> mergeInfoList(Long id, List<TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq> reqList) {
        List<TbVmsMsgDsplSchDto> result = new ArrayList<>();

        HashSet<String> trfcSituTypeCdSet = new HashSet<>();
        reqList.forEach(obj -> {
            // 제어기 ID를 모두 동일하게 설정
            if (!Objects.equals(id, obj.getVmsCtlrNmbr())) {
                log.error("VmsCtlrNmbr different: {}, {}", id, obj.getVmsCtlrNmbr());
                obj.setVmsCtlrNmbr(id);
            }
            trfcSituTypeCdSet.add(obj.getTrfcSituTypeCd());
        });

        /**
         * 기존 데이터를 모두 삭제
         */
        List<String> trfcSituTypeCd = new ArrayList<>(trfcSituTypeCdSet);
        this.repo.deleteDataByVmsCtrlTrfcSituTypeCd(id, trfcSituTypeCd);

        /**
         * 요청 데이터 저장
         */
        for (TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq req : reqList) {
            TbVmsMsgDsplSch obj = req.toEntity();
            this.repo.save(obj);    // INSERT
            result.add(obj.toDto());
        }
        return result;
    }

    /**
     * VMS 폼 표출 스케줄 일괄 등록(전체 VMS 등록 - 저장)
     * 신규 데이터를 저장한다.(INSERT only)
     * PHASE 번호를 증가 시켜야 한다. 재난안전은 스케줄당 1개가 존재해야 하므로 기존것에 덮어 저장한다(UPDATE)
     * @param reqList
     * @return
     */
    @Transactional
    public List<TbVmsMsgDsplSchDto> batchCreateInfoList(List<TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq> reqList) {
        List<TbVmsMsgDsplSchDto> result = new ArrayList<>();
        for (TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq req : reqList) {
            TbVmsMsgDsplSch obj = req.toEntity();
            if (!("8".equals(req.getTrfcSituTypeCd()))) {
                // 재난 안전 폼이 아닌경우 해당 폼 유형에서 최대 PHASE 로 설정 한다.
                int maxPhase = this.repo.findMaxPhaseByVmsCtrlTrfcSituTypeCd(req.getVmsCtlrNmbr(), req.getTrfcSituTypeCd());
                obj.setPhase(maxPhase);
                log.error("MAX Phase: {}, {}, {}", maxPhase, req.getVmsCtlrNmbr(), req.getTrfcSituTypeCd());
            }
            this.repo.save(obj);    // INSERT only
            result.add(obj.toDto());
        }
        return result;
    }

    /**
     * VMS 폼 표출 스케줄 일괄 등록(전체 VMS 편집 - 저장)
     * 데이터를 저장한다.(INSERT or UPDATE)
     * @param reqList
     * @return
     */
    @Transactional
    public List<TbVmsMsgDsplSchDto> batchMergeInfoList(List<TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq> reqList) {
        List<TbVmsMsgDsplSchDto> result = new ArrayList<>();
        for (TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchUpdReq req : reqList) {
            TbVmsMsgDsplSch obj = req.toEntity();
            this.repo.save(obj);    // INSERT or UPDATE
            result.add(obj.toDto());
        }
        return result;
    }

    /**
     * 스케쥴 정보 일괄삭제(전체 VMS 편집 - 삭제)
     * @param ids
     * @return
     */
    @Transactional
    public List<TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchDelReq> batchDeleteByIds(List<TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchDelReq> ids) {
        List<TbVmsMsgDsplSchDto.TbVmsTrfcInfrDsplSchDelReq> result = new ArrayList<>();
        ids.forEach(obj -> {
            TbVmsMsgDsplSchKey key = new TbVmsMsgDsplSchKey(obj.getVmsCtlrNmbr(), obj.getTrfcSituTypeCd(), obj.getPhase());
            try {
                this.repo.deleteById(key);
                result.add(obj);
            }
            catch (Exception e) {
                log.error("Not Found: {}", key);
            }
        });
        return result;
    }

    /**
     * VMS 일괄 편집 시 VMS 목록 조회
     * @param vmsSizeType
     * @param trfcSituTypeCd
     * @param vmsFormId
     * @param isEdit
     * @return
     */
    public List<TbVmsMsgDsplSchVmsListDto> findAllByAllEditVmsList(String vmsSizeType, String trfcSituTypeCd, Integer vmsFormId, boolean isEdit) {
        List<TbVmsMsgDsplSchVmsListDto> result = new ArrayList<>();

        Map<Long, TbVmsCtlr> vmsMap = new HashMap<>();
        List<TbVmsCtlr> vmsList = this.vmsRepo.findAllListByTypeCd(vmsSizeType);
        vmsList.forEach(obj -> {
            vmsMap.put(obj.getVmsCtlrNmbr(), obj);
        });
        //Map<Long, TbVmsCtlr> vmsList = vmsList.stream().collect(Collectors.toMap(TbVmsCtlr::getVmsCtlrNmbr, Function.identity()));

        Set<Long> schSet = new HashSet<>();
        List<TbVmsMsgDsplSch> data = this.repo.findAllByAllEditVmsList(trfcSituTypeCd, vmsFormId);
        data.forEach(obj -> {
            TbVmsMsgDsplSchVmsListDto dto = obj.toVmsListDto();
            log.error("EDT: {}", obj.getVmsCtlrNmbr());
            schSet.add(dto.getVmsCtlrNmbr());
            dto.setVmsInfo(vmsMap.get(dto.getVmsCtlrNmbr()), isEdit);
            result.add(dto);
        });

        if (!isEdit) {
            /** 일괄 등록 인 경우 스케줄에 등록되지 않은 VMS 를 추가해 준다. **/
            vmsList.forEach(obj -> {
                if (schSet.add(obj.getVmsCtlrNmbr())) {
                    log.error("ADD: {}", obj.getVmsCtlrNmbr());
                    /** 등록되어 있지 않음 VMS 임 **/
                    TbVmsMsgDsplSchVmsListDto dto = TbVmsMsgDsplSchVmsListDto.builder()
                            .vmsCtlrNmbr(obj.getVmsCtlrNmbr())
                            .trfcSituTypeCd(trfcSituTypeCd)
                            .phase(1)
                            .vmsFormId(vmsFormId)
                            .dsplHh(4)
                            .dsplStrtHh(ItsUtils.getSysTime())
                            .dsplEndHh("20991231235959")
                            .useYn("Y")
                            .vmsCtlrId(obj.getVmsCtlrId())
                            .vmsNm(obj.getVmsNm())
                            .selectYn("Y")
                            .build();
                    result.add(dto);
                }
            });
        }
        return result;
    }
}
