package tech.easyflow.system.service.impl;

import tech.easyflow.common.util.SqlOperatorsUtil;
import tech.easyflow.system.entity.*;
import tech.easyflow.system.mapper.SysMenuMapper;
import tech.easyflow.system.service.SysAccountRoleService;
import tech.easyflow.system.service.SysMenuService;
import tech.easyflow.system.service.SysRoleMenuService;
import cn.hutool.core.collection.CollectionUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.query.SqlOperators;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 菜单表 服务层实现。
 *
 * @author ArkLight
 * @since 2025-03-14
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Resource
    private SysRoleMenuService sysRoleMenuService;
    @Resource
    private SysAccountRoleService sysAccountRoleService;

    /**
     * 根据账号查询菜单，并自动补齐已授权节点的父级菜单链。
     * <p>
     * 这样当角色只勾选了某个页面下的按钮权限或子能力时，
     * 其所属的页面菜单仍能正常出现在侧边栏中，避免出现“有子权限但无入口”的问题。
     *
     * @param entity    菜单过滤条件
     * @param accountId 账号 ID
     * @return 当前账号可访问的菜单集合
     */
    @Override
    public List<SysMenu> getMenusByAccountId(SysMenu entity, BigInteger accountId) {
        // 查询用户对应角色id集合
        QueryWrapper am = QueryWrapper.create();
        am.eq(SysAccountRole::getAccountId, accountId);
        List<BigInteger> roleIds = sysAccountRoleService.list(am).stream().map(SysAccountRole::getRoleId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(roleIds)) {
            return new ArrayList<>();
        }
        // 查询角色对应的菜单id集合
        QueryWrapper rm = QueryWrapper.create();
        rm.in(SysRoleMenu::getRoleId, roleIds);
        List<BigInteger> menuIds = sysRoleMenuService.list(rm).stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(menuIds)) {
            return new ArrayList<>();
        }
        List<BigInteger> fullMenuIds = collectMenuIdsWithParents(menuIds);
        // 查询当前用户拥有的菜单
        SqlOperators ops = SqlOperatorsUtil.build(SysMenu.class);
        QueryWrapper queryWrapper = QueryWrapper.create(entity, ops);
        queryWrapper.in(SysMenu::getId, fullMenuIds);
        queryWrapper.orderBy("sort_no asc");
        return list(queryWrapper);
    }

    /**
     * 收集菜单自身及其所有父级菜单 ID。
     *
     * @param menuIds 已授权的菜单 ID 列表
     * @return 包含父级链路的完整菜单 ID 列表
     */
    private List<BigInteger> collectMenuIdsWithParents(List<BigInteger> menuIds) {
        List<SysMenu> allMenus = list();
        if (CollectionUtil.isEmpty(allMenus)) {
            return menuIds;
        }
        Map<BigInteger, SysMenu> menuMap = new HashMap<>();
        for (SysMenu menu : allMenus) {
            menuMap.put(menu.getId(), menu);
        }
        Set<BigInteger> result = new HashSet<>(menuIds);
        for (BigInteger menuId : menuIds) {
            appendParentMenuIds(menuId, menuMap, result);
        }
        return new ArrayList<>(result);
    }

    /**
     * 递归追加父级菜单 ID，直到根节点或无父节点为止。
     *
     * @param menuId   当前菜单 ID
     * @param menuMap  全量菜单映射
     * @param result   结果集合
     */
    private void appendParentMenuIds(BigInteger menuId, Map<BigInteger, SysMenu> menuMap, Set<BigInteger> result) {
        SysMenu current = menuMap.get(menuId);
        if (current == null) {
            return;
        }
        BigInteger parentId = current.getParentId();
        if (parentId == null || BigInteger.ZERO.equals(parentId) || !result.add(parentId)) {
            return;
        }
        appendParentMenuIds(parentId, menuMap, result);
    }
}
