package com.its.op.service.its.cctv;

import com.its.op.dao.repository.its.cctv.TbCctvPsetRepository;
import com.its.op.dto.its.cctv.TbCctvCtlrDto;
import com.its.op.dto.its.cctv.TbCctvPsetDto;
import com.its.op.entity.its.cctv.TbCctvPset;
import com.its.op.entity.its.cctv.TbCctvPsetKey;
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 TbCctvPsetService {

    private final TbCctvCtlrService cctvService;
    private final TbCctvPsetRepository repo;
    private final int MAX_PRESET = 16;

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

    public Map<String, TbCctvPsetDto> getInitPsetInfo(Long cctvCtlrNmbr) {
        Map<String, TbCctvPsetDto> psetMap = new HashMap<>();
        for (int ii = 1; ii <= this.MAX_PRESET; ii++) {
            psetMap.put(String.valueOf(ii), TbCctvPsetDto.builder()
                    .cctvCtlrNmbr(cctvCtlrNmbr)
                    .psetNm("")
                    .psetNmbr(String.valueOf(ii))
                    .panVal(0)
                    .tiltVal(0)
                    .zoomVal(0)
                    .focsVal(0)
                    .dfltYn("N")
                    .build());
        }
        return psetMap;
    }
    /**
     * 프리셋 전체 조회
     * @return
     */
    @Transactional(readOnly = true)
    public List<TbCctvPsetDto> findAll() {

        List<TbCctvPsetDto> result = new ArrayList<>();
        Map<Long, Map<String, TbCctvPsetDto>> cctvMap = new HashMap<>();
        List<TbCctvCtlrDto> cctv = this.cctvService.findAllList();
        cctv.forEach(obj -> {
            cctvMap.put(obj.getCctvCtlrNmbr(), getInitPsetInfo(obj.getCctvCtlrNmbr()));
        });

        List<TbCctvPset> data = this.repo.findAll();
        data.forEach(obj -> {
            TbCctvPsetDto dto = obj.toDto();
            Map<String, TbCctvPsetDto> psetMap = cctvMap.get(obj.getCctvCtlrNmbr());
            if (psetMap != null) {
                psetMap.put(obj.getPsetNmbr(), dto);
            }
        });
        cctvMap.forEach((key, value) -> {
            value.forEach((psetNmbr, dto) -> {
                result.add(dto);
            });
        });
        return result;
    }

    /**
     * 해당 CCTV 의 프리셋 정보를 조회한다.
     * 프리셋 정보가 존재하지 않음면 기본 프리셋 정보를 리턴한다.
     * @param id
     * @return
     */
    @Transactional(readOnly = true)
    public List<TbCctvPsetDto> findListById(Long id) {
        List<TbCctvPsetDto> result = new ArrayList<>();
        Map<String, TbCctvPsetDto> defPset = getInitPsetInfo(id);
        List<TbCctvPset> data = this.repo.findListById(id);
        data.forEach(obj -> {
            TbCctvPsetDto dto = obj.toDto();
            defPset.put(obj.getPsetNmbr(), dto);
        });
        defPset.forEach((key, dto) -> {
            result.add(dto);
        });
        return result;
    }

    // 데이터 변경
    @Transactional
    public TbCctvPsetDto updateById(Long id, TbCctvPsetDto.TbCctvPsetUpdReq req) {
        TbCctvPsetKey key = new TbCctvPsetKey(id, req.getPsetNmbr());
        TbCctvPset entity = requireOne(key);
        entity.updateInfo(req);
        this.repo.save(entity);
        return entity.toDto();
    }

    // 데이터 변경 또는 생성-목록(데이터가 존재하면 업데이트 없으면 신규로 생성)
    @Transactional
    public List<TbCctvPsetDto> mergeInfoList(List<TbCctvPsetDto.TbCctvPsetUpdReq> reqList) {
        List<TbCctvPsetDto> result = new ArrayList<>();
        for (TbCctvPsetDto.TbCctvPsetUpdReq req : reqList) {
            TbCctvPset obj = req.toEntity();
            this.repo.save(obj);
            result.add(obj.toDto());
        }
        return result;
    }

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

    // 정보 삭제-개별, 데이터 존재하지 않으면 Exception

    /**
     * 해당 프리셋 정보를 삭제한다(빈정보로 업데이트)
     * @param id
     * @param psetNmbr
     * @return
     */
    @Transactional
    public TbCctvPsetDto deleteById(Long id, String psetNmbr) {
        TbCctvPset entity = TbCctvPset.builder()
                .cctvCtlrNmbr(id)
                .psetNmbr(psetNmbr)
                .psetNm("")
                .tiltVal(0)
                .panVal(0)
                .focsVal(0)
                .zoomVal(0)
                .dfltYn("N")
                .build();
        this.repo.save(entity);
        return entity.toDto();
    }

    // 정보 삭제-목록, 존재하는 데이터 만 삭제
    @Transactional
    public List<TbCctvPsetDto> deleteByIds(List<TbCctvPsetKey> ids) {
        List<TbCctvPsetDto> result = new ArrayList<>();
        for (TbCctvPsetKey id : ids) {
            Optional<TbCctvPset> obj = this.repo.findById(id);
            if (obj.isPresent()) {
                this.repo.deleteById(id);
                result.add(obj.get().toDto());
            }
        }
        return result;
    }

}
