package com.its.api.its.service.ifsc;

import com.its.api.its.global.CodeManager;
import com.its.api.its.model.dto.code.TbCmmnCdDto;
import com.its.api.its.model.dto.ifsc.TbReptCngsSectDto;
import com.its.api.its.model.dto.ifsc.TbReptCngsSectKeyDto;
import com.its.api.its.model.dto.ifsc.TbReptCngsSectTrafDto;
import com.its.api.its.model.entity.ifsc.TbReptCngsSect;
import com.its.api.its.model.entity.ifsc.TbReptCngsSectKey;
import com.its.api.its.model.entity.ifsc.TbReptCngsSectTraf;
import com.its.api.its.repository.ifsc.TbReptCngsSectRepository;
import com.its.api.its.repository.ifsc.TbReptCngsSectTrafRepository;
import com.its.api.its.service.code.TbCmmnCdService;
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.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
@Service
public class TbReptCngsSectService {

    private final TbReptCngsSectRepository repo;
    private final TbReptCngsSectTrafRepository trafRepo;
    private final TbCmmnCdService cmmnCdService;

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

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

    /**
     * 반복정체관리 - 현재 반복정체 정보 조회
     * @param dcsnYn
     * @return
     */
    @Transactional(readOnly = true)
    public List<TbReptCngsSectDto> findAllCurrent(String dcsnYn) {
        //String dayTypeCd = ItsUtils.getDayOfWeek();
        List<TbReptCngsSectDto> result = new ArrayList<>();
        List<TbReptCngsSect> data = this.repo.findAllCurrent(dcsnYn, ItsUtils.getDayOfWeek());
        for (TbReptCngsSect entity : data) {
            result.add(entity.toDto());
        }
        return result;
    }

    /**
     * 반복정체관리 - 현재 반복정체 정보 조회(소통정보 포함)
     * 지도 화면 반복정체구간 목록에서 조회
     * @param
     * @return
     */
    @Transactional(readOnly = true)
    public List<TbReptCngsSectTrafDto> findAllCurrentTraf() {
        List<TbReptCngsSectTrafDto> result = new ArrayList<>();
        List<TbReptCngsSectTraf> data = this.trafRepo.findAllCurrent("Y", ItsUtils.getDayOfWeek());
        for (TbReptCngsSectTraf entity : data) {
            result.add(entity.toDto());
        }
        return result;
    }

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

    /**
     * 반복정체구간관리 - 운영자가 입력한 경우 확정으로 데이터 저장
     * @param reqList
     * @return
     */
    @Transactional
    public List<TbReptCngsSectDto> mergeInfoList(List<TbReptCngsSectDto.TbReptCngsSectUpdReq> reqList) {
        List<TbReptCngsSectDto> result = new ArrayList<>();
        for (TbReptCngsSectDto.TbReptCngsSectUpdReq req : reqList) {
            req.setStatYm(ItsUtils.getSysMonth());
            req.setDcsnYn("Y");
            req.setReptCngsSectOrgnCd("RCS2");
            req.setCrtnYmd(ItsUtils.getSysDay());

            TbReptCngsSect obj = req.toEntity();
            this.repo.save(obj);
            result.add(obj.toDto());
        }
        return result;
    }

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

    /**
     * 반복정체구간 - 확정취소, 예정취소 : 데이터 삭제
     * @param ids
     * @return
     */
    @Transactional
    public List<TbReptCngsSectKeyDto> deleteByIds(List<TbReptCngsSectKeyDto> ids) {
        for (TbReptCngsSectKeyDto id : ids) {
            TbReptCngsSectKey key = new TbReptCngsSectKey(id.getIfscId(), id.getStatYm(), id.getDayTypeCd(), id.getCnfsStrtHm());
            Optional<TbReptCngsSect> obj = this.repo.findById(key);
            if (obj.isPresent()) {
                this.repo.deleteById(key);
            }
        }
        return ids;
    }

    /**
     * 반복정체구간관리 - 반복정체구간 확정
     * @param ids
     * @return
     */
    public List<TbReptCngsSectKeyDto> cnfmInfoList(List<TbReptCngsSectKeyDto> ids) {
        for (TbReptCngsSectKeyDto id : ids) {
            TbReptCngsSectKey key = new TbReptCngsSectKey(id.getIfscId(), id.getStatYm(), id.getDayTypeCd(), id.getCnfsStrtHm());
            Optional<TbReptCngsSect> obj = this.repo.findById(key);
            if (obj.isPresent()) {
                // 데이터가 있는 경우에만 확정 플래그 설정해서 업데이트
                obj.get().cnfm();
                this.repo.save(obj.get());
            }
        }
        return ids;
    }


    /**
     * 반복정체구간 분석: 반복정체 요일 유형 코드 목록 조회
     * @return
     */
    @Transactional(readOnly = true)
    public List<TbReptCngsSectDto.ReptCngsDayType> findAllDayType() {
        List<TbReptCngsSectDto.ReptCngsDayType> result = new ArrayList<>();
        TbCmmnCdDto allEntity = TbCmmnCdDto.builder().cmmnCd("ALL").cmmnCdKorNm("전체").build();
        TbReptCngsSectDto.ReptCngsDayType all = new TbReptCngsSectDto.ReptCngsDayType(allEntity);
        result.add(all);

        List<TbCmmnCdDto> data = this.cmmnCdService.findAllByCmmnClsfCd(CodeManager.DAY_TYPE_CD);
        if (data != null) {
            data.forEach(obj -> {
                if (obj.getUseYn().equals("Y")) {
                    result.add(new TbReptCngsSectDto.ReptCngsDayType(obj));
                }
            });
        }
        return result;
    }

    /**
     * 반복정체구간 분석: 반복정체구간 이력 조회
     * @param dayTypeCd
     * @param baseMn
     * @return
     */
    public List<TbReptCngsSectDto> findAllAnalByDayTypeCdAndStatYm(String dayTypeCd, String baseMn) {
        List<TbReptCngsSectDto> result = new ArrayList<>();

        List<TbReptCngsSect> data = null;
        if ("ALL".equals(dayTypeCd)) {
            data = this.repo.findAllAnalByStatYm(baseMn);
        }
        else {
            data = this.repo.findAllAnalByDayTypeCdAndStatYm(dayTypeCd, baseMn);
        }
        if (data != null) {
            data.forEach(obj -> {
                result.add(obj.toDto());
            });
        }
        return result;
    }
}
