package com.its.api.its.service.oper;

import com.its.api.its.model.dto.oper.TbGropMenuAthrDto;
import com.its.api.its.model.entity.oper.TbGropMenuAthr;
import com.its.api.its.model.entity.oper.TbGropMenuAthrKey;
import com.its.api.its.model.entity.oper.TbOperSystMenu;
import com.its.api.its.model.entity.oper.TbUserGropInfr;
import com.its.api.its.repository.oper.TbGropMenuAthrRepository;
import com.its.api.its.repository.oper.TbOperSystMenuRepository;
import com.its.api.its.repository.oper.TbUserGropInfrRepository;
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 TbGropMenuAthrService {

    private final TbGropMenuAthrRepository repo;
    private final TbUserGropInfrRepository userGropRepo;
    private final TbOperSystMenuRepository systMenuRepo;

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

    // 전체 데이터 조회
    @Transactional(readOnly = true)
    public List<TbGropMenuAthrDto> findAll() {
        List<TbGropMenuAthrDto> result = new ArrayList<>();

        List<TbUserGropInfr> userGrops = this.userGropRepo.findAll();
        List<TbOperSystMenu> systMenus = this.systMenuRepo.findAll();
        List<TbGropMenuAthr> menuAuths = this.repo.findAll();

        // 메뉴 중에 최상위 메뉴 선택
        HashMap<String, String> systMenuMap = new HashMap<>();
        for (TbOperSystMenu systMenu : systMenus) {
            if (("N").equals(systMenu.getDelYn()) & systMenu.getMenuLevl() == 1) {
                // 레벨이 1 인 것이 최상위 메뉴이다, OPERSYST_MENU_ID, OPER_SYST_ID 는 NULL 로 들어가 있다.
                systMenuMap.put(systMenu.getOperSystMenuId(), systMenu.getMenuName());
            }
        }

        // 등록되어 있는 권한 메모리 저장
        HashMap<String, TbGropMenuAthr> authMap = new HashMap<>();
        for (TbGropMenuAthr menuAthr : menuAuths) {
            authMap.put(menuAthr.getGropId() + "*" + menuAthr.getOperSystMenuId(), menuAthr);
        }

        // 권한별로 모든 메뉴 생성
        for (TbUserGropInfr userGrop : userGrops) {
            if (("Y").equals(userGrop.getDelYn())) {
                continue;
            }

            // 모든 메뉴에 대해서
            for (TbOperSystMenu systMenu : systMenus) {
                if (("Y").equals(systMenu.getDelYn()) || systMenu.getMenuLevl() == 1) {
                    // 메뉴레벨이 1인 최상위 메뉴는 제외한다.
                    continue;
                }

                // 최상위 메뉴명을 업데이트
                systMenu.setOperSystNm(systMenuMap.get(systMenu.getOperSystId()));
                // 메뉴권한 등록
                TbGropMenuAthrDto menuAthr = TbGropMenuAthr.toDtoByMenu(userGrop, systMenu);
                TbGropMenuAthr auth = authMap.get(userGrop.getGropId() + "*" + systMenu.getOperSystMenuId());
                if (auth != null) {
                    // 메뉴권한이 등록되어 있으면 업데이트
                    menuAthr.updateMenuAuth(auth);
                }
                result.add(menuAthr);
            }
        }
        return result;
    }

    // 데이터 1건 조회(기존 데이터가 반드시 존재해야 함)
    @Transactional(readOnly = true)
    public TbGropMenuAthrDto findById(TbGropMenuAthrKey id) {
        TbGropMenuAthr entity = requireOne(id);
        return entity.toDto();
    }
/*
public List<GropMenuAthrDto.GropMenuAthrInfo> findById(String id) {
        List<GropMenuAthrDto.GropMenuAthrInfo> result = new ArrayList<>();
        try {
            Optional<TbUserGropInfr> userGrops = this.userGropRepo.findById(id);
            List<OperSystMenu> systMenus = this.systMenuRepo.findAll();
            List<GropMenuAthr> menuAuths = this.menuAuthRepo.findAll();
            HashMap<String, GropMenuAthr> authMap = new HashMap<>();
            for (GropMenuAthr menuAthr : menuAuths) {
                authMap.put(menuAthr.getGROP_ID() + "*" + menuAthr.getOPERSYST_MENU_ID(), menuAthr);
            }

            if (userGrops.isPresent()) {
                TbUserGropInfr userGrop = userGrops.get();
                if (userGrop.getDelYn().equals("N")) {
                    for (OperSystMenu systMenu : systMenus) {
                        if (systMenu.getDEL_YN().equals("N")) {
                            GropMenuAthrDto.GropMenuAthrInfo menuAthr = new GropMenuAthrDto.GropMenuAthrInfo(userGrop, systMenu);
                            GropMenuAthr auth = authMap.get(userGrop.getGropId() + "*" + systMenu.getOPERSYST_MENU_ID());
                            if (auth != null) {
                                menuAthr.updateMenuAuth(auth);
                            }
                            result.add(menuAthr);
                        }
                    }
                }
            }
        }
        catch (Exception e) {
            log.error("{}.findById: Exception: {}", getClass().getSimpleName(), e.getMessage());
        }
        return result;
    } */
    // 데이터 변경
    @Transactional
    public TbGropMenuAthrDto updateById(TbGropMenuAthrKey id, TbGropMenuAthrDto.TbGropMenuAthrUpdReq req) {
        TbGropMenuAthr entity = requireOne(id);
        entity.updateInfo(req);
        this.repo.save(entity);
        return entity.toDto();
    }

    // 데이터 변경 또는 생성-목록(데이터가 존재하면 업데이트 없으면 신규로 생성)
    @Transactional
    public List<TbGropMenuAthrDto> mergeInfoList(List<TbGropMenuAthrDto.TbGropMenuAthrUpdReq> reqList) {
        List<TbGropMenuAthrDto> result = new ArrayList<>();
        for (TbGropMenuAthrDto.TbGropMenuAthrUpdReq req : reqList) {
            //TbGropMenuAthrKey key = new TbGropMenuAthrKey(req.getGropId(), req.getOperSystMenuId());
            //Optional<TbGropMenuAthr> data = this.repo.findById(key);
            TbGropMenuAthr obj = req.toEntity();
            this.repo.save(obj);
            result.add(obj.toDto());
        }

        return result;
    }

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

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

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

}
