package com.zioinfo.hrm.uiws.system.service;

import com.zioinfo.hrm.uiws.common.UiwsApiException;
import com.zioinfo.hrm.uiws.common.UiwsErrorCode;
import com.zioinfo.hrm.uiws.system.dto.MenuDto;
import com.zioinfo.hrm.uiws.system.dto.MenuSaveDto;
import com.zioinfo.hrm.uiws.system.dto.PageResponse;
import com.zioinfo.hrm.uiws.system.mapper.MenuMapper;
import com.zioinfo.hrm.uiws.system.mapper.RoleMenuMapper;
import com.zioinfo.hrm.uiws.system.model.SysMenu;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/** 2.9 메뉴(menus) CRUD + 검색팝업 + 다중삭제. 프론트가 parentMenuId 로 트리 구성. */
@Service
@RequiredArgsConstructor
public class MenuService {

    private final MenuMapper menuMapper;
    private final RoleMenuMapper roleMenuMapper;

    @Transactional(readOnly = true)
    public PageResponse<MenuDto> list(int page, int size) {
        List<MenuDto> content = menuMapper.searchAll(page * size, size).stream().map(this::toDto).toList();
        return PageResponse.of(content, menuMapper.countAll(), page, size);
    }

    @Transactional(readOnly = true)
    public List<MenuDto> searchPopup(String keyword) {
        return menuMapper.search(keyword).stream().map(this::toDto).toList();
    }

    @Transactional(readOnly = true)
    public MenuDto get(String menuId) {
        return toDto(find(menuId));
    }

    @Transactional
    public MenuDto create(MenuSaveDto dto) {
        if (dto.menuId() == null || dto.menuId().isBlank()) {
            throw new UiwsApiException(UiwsErrorCode.INVALID_REQUEST, "menuId는 등록 시 필수입니다.");
        }
        if (menuMapper.existsById(dto.menuId())) {
            throw new UiwsApiException(UiwsErrorCode.DUPLICATE_KEY, "이미 존재하는 메뉴 ID입니다.");
        }
        SysMenu m = new SysMenu();
        m.setMenuId(dto.menuId());
        apply(m, dto);
        m.setCreatedBy(SysActor.id());
        menuMapper.insert(m);
        return toDto(find(dto.menuId()));
    }

    @Transactional
    public MenuDto update(String menuId, MenuSaveDto dto) {
        SysMenu m = find(menuId);
        apply(m, dto);
        m.setUpdatedBy(SysActor.id());
        menuMapper.update(m);
        return toDto(find(menuId));
    }

    @Transactional
    public void delete(List<String> ids) {
        for (String id : ids) {
            if (!menuMapper.existsById(id)) {
                throw new UiwsApiException(UiwsErrorCode.MENU_NOT_FOUND);
            }
            if (menuMapper.existsByParentMenuId(id)) {
                throw new UiwsApiException(UiwsErrorCode.RESOURCE_IN_USE, "하위 메뉴가 존재하여 삭제할 수 없습니다: " + id);
            }
            if (roleMenuMapper.existsByMenuId(id)) {
                throw new UiwsApiException(UiwsErrorCode.RESOURCE_IN_USE, "권한에 매핑된 메뉴는 삭제할 수 없습니다: " + id);
            }
            menuMapper.deleteById(id);
        }
    }

    private void apply(SysMenu m, MenuSaveDto dto) {
        m.setMenuNm(dto.menuNm());
        m.setParentMenuId(dto.parentMenuId());
        m.setProgramId(dto.programId());
        m.setMenuUrl(dto.menuUrl());
        m.setSortOrd(dto.sortOrd() == null ? 0 : dto.sortOrd());
        m.setUseYn(dto.useYn());
    }

    private MenuDto toDto(SysMenu m) {
        return new MenuDto(m.getMenuId(), m.getMenuNm(), m.getParentMenuId(), m.getProgramId(),
                m.getMenuUrl(), m.getSortOrd() == null ? 0 : m.getSortOrd(), m.getUseYn());
    }

    private SysMenu find(String menuId) {
        SysMenu m = menuMapper.findById(menuId);
        if (m == null) {
            throw new UiwsApiException(UiwsErrorCode.MENU_NOT_FOUND);
        }
        return m;
    }
}
