package go.kr.project.system.menu.service.impl;

import egovframework.exception.MessageException;
import go.kr.project.system.menu.mapper.MenuMapper;
import go.kr.project.system.menu.model.MenuSearchVO;
import go.kr.project.system.menu.model.MenuVO;
import go.kr.project.system.menu.service.MenuService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * packageName    : go.kr.project.system.menu.service.impl
 * fileName       : MenuServiceImpl
 * author         : 개발자
 * date           : 2023-06-01
 * description    : 메뉴 관련 비즈니스 로직을 처리하는 서비스 구현체
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2023-06-01        개발자       최초 생성
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends EgovAbstractServiceImpl implements MenuService {

    private final MenuMapper menuMapper;

    /**
     * 메뉴 ID를 자동 생성합니다.
     * 
     * @param menuLevel 메뉴 레벨
     * @param upperMenuId 상위 메뉴 ID
     * @return 생성된 메뉴 ID
     */
    @Override
    public String generateMenuId(int menuLevel, String upperMenuId) {
        // ROOT 메뉴인 경우 그대로 반환
        if (menuLevel == 0 && (upperMenuId == null || upperMenuId.isEmpty())) {
            return "ROOT";
        }

        // 데이터베이스 쿼리를 사용하여 메뉴 ID 생성
        return menuMapper.generateMenuIdByQuery(menuLevel, upperMenuId);
    }

    /**
     * 메뉴 정보를 등록합니다.
     * 
     * @param vo 등록할 메뉴 정보를 담은 VO 객체
     * @return 등록된 행의 수
     */
    @Override
    public int insertMenu(MenuVO vo) {
        // 메뉴 ID 자동 생성
        String menuId = generateMenuId(vo.getMenuLevel(), vo.getUpperMenuId());
        vo.setMenuId(menuId);

        return menuMapper.insertMenu(vo);
    }

    /**
     * 메뉴 정보를 수정합니다.
     * 
     * @param vo 수정할 메뉴 정보를 담은 VO 객체
     * @return 수정된 행의 수
     */
    @Override
    public int updateMenu(MenuVO vo) {
        return menuMapper.updateMenu(vo);
    }

    /**
     * 메뉴 정보를 삭제합니다.
     * 메뉴 삭제 전에 메뉴가 사용 중인지 확인하고, 사용 중이 아닌 경우에만 삭제합니다.
     * 메뉴 삭제 전에 역할-메뉴 매핑을 먼저 삭제합니다.
     * 
     * @param menuId 삭제할 메뉴 ID
     * @return 삭제된 행의 수
     * @throws RuntimeException 메뉴가 사용 중인 경우 발생
     */
    @Override
    public int deleteMenu(String menuId) {
        // 메뉴가 사용 중인지 확인
        if (menuMapper.checkMenuInUse(menuId)) {
            // 메뉴를 사용 중인 역할, 그룹, 사용자 정보 조회
            List<Map<String, Object>> usageInfo = menuMapper.getMenuUsageInfo(menuId);

            StringBuilder errorMsg = new StringBuilder("사용 중인 메뉴는 삭제할 수 없습니다: " + menuId);

            if (usageInfo != null && !usageInfo.isEmpty()) {
                // 역할별로 정보 정리
                Map<String, StringBuilder> roleGroups = new HashMap<>();
                Map<String, StringBuilder> roleUsers = new HashMap<>();

                for (Map<String, Object> info : usageInfo) {
                    String roleId = (String) info.get("ROLE_ID");
                    String roleNm = (String) info.get("ROLE_NM");
                    String userGroupId = (String) info.get("GROUP_ID");
                    String groupNm = (String) info.get("GROUP_NM");
                    String userId = (String) info.get("USER_ID");
                    String userNm = (String) info.get("USER_NM");

                    String roleKey = roleId + " (" + roleNm + ")";

                    // 역할에 연결된 그룹 정보 추가
                    if (userGroupId != null && groupNm != null) {
                        if (!roleGroups.containsKey(roleKey)) {
                            roleGroups.put(roleKey, new StringBuilder());
                        }

                        if (roleGroups.get(roleKey).indexOf(userGroupId) == -1) {
                            if (roleGroups.get(roleKey).length() > 0) {
                                roleGroups.get(roleKey).append(", ");
                            }
                            roleGroups.get(roleKey).append(groupNm).append(" (").append(userGroupId).append(")");
                        }
                    }

                    // 역할에 연결된 사용자 정보 추가
                    if (userId != null && userNm != null) {
                        if (!roleUsers.containsKey(roleKey)) {
                            roleUsers.put(roleKey, new StringBuilder());
                        }

                        if (roleUsers.get(roleKey).indexOf(userId) == -1) {
                            if (roleUsers.get(roleKey).length() > 0) {
                                roleUsers.get(roleKey).append(", ");
                            }
                            roleUsers.get(roleKey).append(userNm).append(" (").append(userId).append(")");
                        }
                    }
                }

                // 역할별 그룹 및 사용자 정보 추가
                errorMsg.append("\n다음 역할에서 사용 중입니다:");

                for (String roleKey : roleGroups.keySet()) {
                    errorMsg.append("\n- 역할: ").append(roleKey);

                    if (roleGroups.get(roleKey).length() > 0) {
                        errorMsg.append("\n  그룹: ").append(roleGroups.get(roleKey));
                    }

                    if (roleUsers.containsKey(roleKey) && roleUsers.get(roleKey).length() > 0) {
                        errorMsg.append("\n  사용자: ").append(roleUsers.get(roleKey));
                    }
                }
            }

            throw new MessageException(errorMsg.toString());
        }

        // 메뉴 정보 삭제
        return menuMapper.deleteMenu(menuId);
    }

    /**
     * 하위 메뉴 존재 여부를 확인합니다.
     * 
     * @param menuId 확인할 메뉴 ID
     * @return 하위 메뉴 개수
     */
    @Override
    public int countChildMenus(String menuId) {
        return menuMapper.countChildMenus(menuId);
    }

    /**
     * 메뉴 목록을 트리 구조로 조회합니다.
     * 
     * @param paramVO 검색 조건을 담은 VO 객체
     * @return 트리 구조의 메뉴 목록
     */
    @Override
    public List<MenuVO> selectMenuTreeList(MenuSearchVO paramVO) {
        return menuMapper.selectMenuTreeList(paramVO);
    }
}
