package com.xjrsoft.module.system.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.toolkit.MPJWrappers;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.AuthorizeType;
import com.xjrsoft.common.enums.EnabledMark;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.module.organization.entity.UserRoleRelation;
import com.xjrsoft.module.organization.mapper.UserRoleRelationMapper;
import com.xjrsoft.module.system.dto.MenuTreeDto;
import com.xjrsoft.module.system.dto.RoleMenuServeListDto;
import com.xjrsoft.module.system.entity.Authorize;
import com.xjrsoft.module.system.entity.Menu;
import com.xjrsoft.module.system.entity.Subsystem;
import com.xjrsoft.module.system.entity.XjrMenuQuick;
import com.xjrsoft.module.system.mapper.AuthorizeMapper;
import com.xjrsoft.module.system.mapper.MenuMapper;
import com.xjrsoft.module.system.mapper.XjrMenuDeptRelationMapper;
import com.xjrsoft.module.system.mapper.XjrMenuQuickMapper;
import com.xjrsoft.module.system.service.IMenuService;
import com.xjrsoft.module.system.vo.*;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户 服务实现类
 * </p>
 *
 * @author tzx
 * @since 2022-03-16
 */
@Service
@AllArgsConstructor
public class MenuServiceImpl extends MPJBaseServiceImpl<MenuMapper, Menu> implements IMenuService {

    private final UserRoleRelationMapper userRoleRelationMapper;

    private final AuthorizeMapper authorizeMapper;

    private final XjrMenuQuickMapper menuQuickMapper;

    private final XjrMenuDeptRelationMapper menuDeptRelationMapper;

    @Override
    public List<MenuVo> getAuthMenuList(MenuTreeDto dto) {
        List<Long> roleIds = getUserRoleIds();
        return getAuthMenuByRoleIds(roleIds, dto);
    }


    /**
     * 查询用户菜单服务
     */
    @Override
    public List<MenuServeVo> getAuthMenuServe(MenuTreeDto dto) {
        List<MenuVo> menuVos = getAuthMenuList(dto);
        return getSubMenu(menuVos);
    }

    /*
    获取所有菜单 标记 accessibility
     */
    @Override
    public List<MenuAllServeVo> getAuthAllMenuServe(MenuTreeDto dto) {
        List<Long> roleIdList = new ArrayList<>();
        roleIdList.add(1L);
        List<MenuServeVo> roleMenuList = getSubMenu(getAuthMenuByRoleIds(roleIdList, dto));

        List<MenuServeVo> allMenuList = getSubMenu(getAuthMenuList(dto));
        Map<Long, MenuServeVo> allMenuMap = allMenuList.stream()
                .collect(Collectors.toMap(MenuServeVo::getId, Function.identity()));

        List<MenuAllServeVo> result = roleMenuList.stream()
                .map(menu -> convertToMenuAllServeVo(menu, allMenuMap))
                .collect(Collectors.toList());

        return result;
    }

    private MenuAllServeVo convertToMenuAllServeVo(MenuServeVo menu, Map<Long, MenuServeVo> allMenuMap) {
        MenuAllServeVo menuAllServeVo = new MenuAllServeVo();
        menuAllServeVo.setId(menu.getId());
        menuAllServeVo.setName(menu.getName());
        menuAllServeVo.setMenuVoList(menu.getMenuVoList());
        menuAllServeVo.setAccessibility(allMenuMap.containsKey(menu.getId()) ? 1 : 0);
        return menuAllServeVo;
    }

    /**
     * 查询角色菜单服务
     */
    @Override
    public List<MenuServeVo> getRoleMenuServe(RoleMenuServeListDto dto) {
        // 获取角色菜单
        List<MenuVo> menuVos = getAuthMenuByRoleIds(new ArrayList<Long>() {{
            add(dto.getRoleId());
        }}, new MenuTreeDto() {{
            setSystemType(dto.getSystemType());
        }});
        List<MenuServeVo> result = getSubMenu(menuVos);
        // 获取角色快捷菜单
        List<XjrMenuQuick> menuQuicks = menuQuickMapper.selectList(Wrappers.lambdaQuery(XjrMenuQuick.class).eq(XjrMenuQuick::getRoleId, dto.getRoleId()));

        // 处理数据是否被选中
        result.forEach((node) -> {
            node.getMenuVoList().forEach((menu) -> {
                menuQuicks.stream().filter(x -> x.getMenuId().equals(menu.getId()))
                        .findAny()
                        .ifPresent(present -> {
                            menu.setIsSelect(true);
                        });
            });
        });
        return result;
    }

    /**
     * 获取用户快捷菜单
     *
     * @return
     */
    @Override
    public List<Menu> getUserQuick(MenuTreeDto dto) {
        Long userId = StpUtil.getLoginIdAsLong();

        List<Menu> menus = this.selectJoinList(Menu.class,
                MPJWrappers.<Menu>lambdaJoin()
                        .innerJoin(XjrMenuQuick.class, XjrMenuQuick::getMenuId, Menu::getId)
                        .eq(XjrMenuQuick::getUserId, userId)
                        .eq(ObjectUtil.isNotEmpty(dto.getSystemType()), Menu::getSystemType, dto.getSystemType())
                        .selectAs(XjrMenuQuick::getSystemId, Menu::getSystemId)
                        .select(Menu.class, x -> VoToColumnUtil.fieldsToColumns(MenuListVo.class).contains(x.getProperty())));
        // 取角色菜单
        if (menus.size() == 0) {
            List<Long> roleIds = getUserRoleIds();
            menus = this.selectJoinList(Menu.class,
                    MPJWrappers.<Menu>lambdaJoin()
                            .innerJoin(XjrMenuQuick.class, XjrMenuQuick::getMenuId, Menu::getId)
                            .in(XjrMenuQuick::getRoleId, roleIds)
                            .eq(ObjectUtil.isNotEmpty(dto.getSystemType()), Menu::getSystemType, dto.getSystemType())
                            .selectAs(XjrMenuQuick::getSystemId, Menu::getSystemId)
                            .select(Menu::getId)
                            .select(Menu.class, x -> VoToColumnUtil.fieldsToColumns(Menu.class).contains(x.getProperty())));
            // 去重
            menus = menus.stream().distinct().collect(Collectors.toList());
        }
        return menus;
    }

    @Override
    public List<MenuTreeVo> selectAllist(List<String> deptIdList) {
        return menuQuickMapper.selectAllList(deptIdList);
    }

    /**
     * 根据角色获取菜单
     *
     * @param roleIds
     * @return
     */
    private List<MenuVo> getAuthMenuByRoleIds(List<Long> roleIds) {
        return getAuthMenuByRoleIds(roleIds, new MenuTreeDto());
    }

    /**
     * 根据角色获取菜单
     *
     * @param roleIds
     * @param dto
     * @return
     */
    private List<MenuVo> getAuthMenuByRoleIds(List<Long> roleIds, MenuTreeDto dto) {
        List<Long> authMenuIdList = null;
        // 非超级管理员权限过滤
        if (!roleIds.contains(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            List<Authorize> authorizeList = authorizeMapper.selectList(Wrappers.<Authorize>lambdaQuery()
                    .eq(Authorize::getAuthorizeType, AuthorizeType.MENU.getCode())
                    .in(Authorize::getRoleId, roleIds));
            if (CollectionUtils.isEmpty(authorizeList)) {
                return new ArrayList<>();
            }
            authMenuIdList = authorizeList.stream().map(Authorize::getObjectId).collect(Collectors.toList());
        }
        List<String> orderList = new ArrayList<>();
        orderList.add("t1.sort_code");orderList.add("t.sort_code");
        List<MenuVo> menuVos = this.selectJoinList(MenuVo.class,
            MPJWrappers.<Menu>lambdaJoin()
            .like(StrUtil.isNotBlank(dto.getTitle()), Menu::getTitle, dto.getTitle())
            .like(StrUtil.isNotBlank(dto.getCode()), Menu::getCode, dto.getCode())
            .like(StrUtil.isNotBlank(dto.getName()), Menu::getName, dto.getName())
            .eq(ObjectUtil.isNotEmpty(dto.getSystemType()), Menu::getSystemType, dto.getSystemType())
            .eq(ObjectUtil.isNotEmpty(dto.getDisplay()), Menu::getDisplay, dto.getDisplay())
            .in(CollectionUtils.isNotEmpty(authMenuIdList), Menu::getId, authMenuIdList)
            .select(Menu::getId)
            .select("1 as authorized")
            .selectAs(Subsystem::getName, MenuVo::getSystemName)
            .select(Menu.class, x -> VoToColumnUtil.fieldsToColumns(MenuTreeVo.class).contains(x.getProperty()))
            .leftJoin(Subsystem.class, Subsystem::getId, Menu::getSystemId)
            .eq(Menu::getEnabledMark, EnabledMark.ENABLED.getCode())
            .orderByAscStr(orderList)
        );
        List<XjrMentDeptGroupVo> menuDept = menuDeptRelationMapper.getMenuDept();
        Map<Long, String> deptNameMap = new HashMap<>();
        Map<Long, String> deptIdMap = new HashMap<>();
        for (XjrMentDeptGroupVo deptGroupVo : menuDept) {
            deptNameMap.put(deptGroupVo.getMenuId(), deptGroupVo.getDeptNames());
            deptIdMap.put(deptGroupVo.getMenuId(), deptGroupVo.getDeptIds());
        }
        for (MenuVo menuVo : menuVos) {
            menuVo.setDeptId(deptIdMap.get(menuVo.getId()));
            menuVo.setDeptName(deptNameMap.get(menuVo.getId()));
        }

        return menuVos;
    }

    /**
     * 获取用户角色
     *
     * @return
     */
    private List<Long> getUserRoleIds() {
        List<UserRoleRelation> relations = userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRoleRelation.class)
                .select(UserRoleRelation::getRoleId)
                .eq(UserRoleRelation::getUserId, StpUtil.getLoginIdAsLong()));
        if (CollectionUtils.isEmpty(relations)) {
            return new ArrayList<>();
        }
        return relations.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toList());
    }

    /**
     * 获取子系统菜单
     *
     * @param menuVos
     * @return
     */
    private List<MenuServeVo> getSubMenu(List<MenuVo> menuVos) {
        Map<Long, MenuServeVo> menuVoMap = new HashMap<>();

        List<MenuVo> childMenus = new ArrayList<>();

        menuVos.forEach((node) -> {
            Long parentId = node.getParentId();

            //找到所有子系统信息
            if (menuVos.stream().noneMatch(x -> StrUtil.equals(x.getId().toString(), String.valueOf(parentId)))) {
                if (!menuVoMap.containsKey(node.getSystemId())) {
                    MenuServeVo ms = new MenuServeVo();
                    ms.setId(node.getSystemId());
                    ms.setName(node.getSystemName());
                    ms.setMenuVoList(new ArrayList<>());
                    menuVoMap.put(node.getSystemId(), ms);
                }
            }

            if (!StrUtil.isEmptyIfStr(parentId) || !StrUtil.equals(String.valueOf(parentId), String.valueOf(GlobalConstant.FIRST_NODE_VALUE))) {
                // 给每条数据设置子系统信息
                menuVos.stream().filter(x -> StrUtil.equals(x.getId().toString(), String.valueOf(parentId)))
                        .findAny()
                        .ifPresent(parentNode -> {
                            node.setSystemId(parentNode.getSystemId());
                            node.setSystemName(parentNode.getSystemName());
                        });
                Long number = menuVos.stream().filter(x -> x.getParentId().equals(node.getId())).count();
                if (number == 0) {
                    childMenus.add(node);
                }
            }
        });
        menuVoMap.values().forEach((node) -> {
            List<MenuVo> childMenu = childMenus.stream().filter(x -> x.getSystemId().equals(node.getId())).collect(Collectors.toList());
            node.setMenuVoList(childMenu);
        });
        return new ArrayList<>(menuVoMap.values());
    }
}
