/*
 * Copyright [2020-2030] [https://www.stylefeng.cn]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Guns采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Guns源码头部的版权声明。
 * 3.请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://gitee.com/stylefeng/guns
 * 5.在修改包名，模块名称，项目代码等时，请注明软件出处 https://gitee.com/stylefeng/guns
 * 6.若您的项目无法满足以上几点，可申请商业授权
 */
package cn.stylefeng.roses.kernel.system.modular.menu.factory;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.stylefeng.roses.kernel.rule.constants.TreeConstants;
import cn.stylefeng.roses.kernel.rule.enums.YesOrNotEnum;
import cn.stylefeng.roses.kernel.rule.tree.factory.DefaultTreeBuildFactory;
import cn.stylefeng.roses.kernel.system.api.AppServiceApi;
import cn.stylefeng.roses.kernel.system.api.pojo.app.SysAppResult;
import cn.stylefeng.roses.kernel.system.api.pojo.menu.MenuAndButtonTreeResponse;
import cn.stylefeng.roses.kernel.system.api.pojo.menu.antd.AntdMenuSelectTreeNode;
import cn.stylefeng.roses.kernel.system.api.pojo.menu.antd.AntdSysMenuDTO;
import cn.stylefeng.roses.kernel.system.api.pojo.role.dto.SysRoleMenuButtonDTO;
import cn.stylefeng.roses.kernel.system.api.pojo.role.dto.SysRoleMenuDTO;
import cn.stylefeng.roses.kernel.system.modular.menu.entity.SysMenu;
import cn.stylefeng.roses.kernel.system.modular.menu.entity.SysMenuButton;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 针对于antd vue版本的前端菜单的组装
 *
 * @author fengshuonan
 * @since 2020/12/30 20:11
 */
public class AntdMenusFactory {

    /**
     * 组装antdv用的获取所有菜单列表详情
     *
     * @param appSortedMenus 按应用排序过的菜单集合
     * @param appNames       排序过的应用名称
     * @author fengshuonan
     * @since 2021/1/7 18:17
     */
    public static List<AntdSysMenuDTO> createTotalMenus(Map<String, List<SysMenu>> appSortedMenus, List<String> appNames) {

        // 创建应用级别的菜单集合
        ArrayList<AntdSysMenuDTO> appSortedAntdMenus = new ArrayList<>();

        // 创建其他应用的菜单
        for (Map.Entry<String, List<SysMenu>> entry : appSortedMenus.entrySet()) {
            // 创建顶层应用菜单
            AntdSysMenuDTO rootAppMenu = createRootAppMenu(entry.getKey());
            List<SysMenu> treeStructMenu = new DefaultTreeBuildFactory<SysMenu>(TreeConstants.DEFAULT_PARENT_ID.toString()).doTreeBuild(entry.getValue());
            List<AntdSysMenuDTO> antdSysMenuDTOS = doModelTransfer(treeStructMenu);

            // 更新顶层应用级别的菜单
            rootAppMenu.setChildren(antdSysMenuDTOS);
            appSortedAntdMenus.add(rootAppMenu);
        }

        // 更新排序
        if (ObjectUtil.isEmpty(appNames)) {
            return appSortedAntdMenus;
        }
        appSortedAntdMenus.sort((antdSysMenuDTO, antdSysMenuDTO2) -> {
            int one = appNames.indexOf(antdSysMenuDTO.getTitle());
            int two = appNames.indexOf(antdSysMenuDTO2.getTitle());
            return Integer.compare(one, two);
        });

        return appSortedAntdMenus;
    }

    /**
     * menu实体转化为菜单树节点
     *
     * @author fengshuonan
     * @since 2020/11/23 21:54
     */
    public static AntdMenuSelectTreeNode parseMenuBaseTreeNode(SysMenu sysMenu) {
        AntdMenuSelectTreeNode menuTreeNode = new AntdMenuSelectTreeNode();
        menuTreeNode.setId(sysMenu.getMenuId());
        menuTreeNode.setParentId(sysMenu.getMenuParentId());
        menuTreeNode.setValue(String.valueOf(sysMenu.getMenuId()));
        menuTreeNode.setTitle(sysMenu.getMenuName());
        menuTreeNode.setWeight(sysMenu.getMenuSort());
        return menuTreeNode;
    }

    /**
     * 添加根节点
     *
     * @author fengshuonan
     * @since 2021/4/16 15:52
     */
    public static AntdMenuSelectTreeNode createRootNode() {
        AntdMenuSelectTreeNode antdMenuSelectTreeNode = new AntdMenuSelectTreeNode();
        antdMenuSelectTreeNode.setId(-1L);
        antdMenuSelectTreeNode.setParentId(-2L);
        antdMenuSelectTreeNode.setTitle("根节点");
        antdMenuSelectTreeNode.setValue(String.valueOf(antdMenuSelectTreeNode.getId()));
        antdMenuSelectTreeNode.setWeight(new BigDecimal(-1));
        return antdMenuSelectTreeNode;
    }

    /**
     * 填充叶子节点的标识
     *
     * @author fengshuonan
     * @since 2021/8/8 15:22
     */
    public static void fillLeafFlag(List<SysMenu> sysMenuList) {
        for (SysMenu sysMenu : sysMenuList) {
            sysMenu.setLeafFlag(true);

            // 判断这个节点下面有没有节点
            for (SysMenu tempMenu : sysMenuList) {
                if (tempMenu.getMenuPids().contains("[" + sysMenu.getMenuId() + "]")) {
                    sysMenu.setLeafFlag(false);
                }
            }
        }
    }

    /**
     * 菜单集合转化成角色分配菜单的集合
     *
     * @author fengshuonan
     * @since 2021/8/10 22:56
     */
    public static List<MenuAndButtonTreeResponse> parseMenuAndButtonTreeResponse(List<SysMenu> sysMenuList, List<SysRoleMenuDTO> roleBindMenus) {
        ArrayList<MenuAndButtonTreeResponse> result = new ArrayList<>();

        if (ObjectUtil.isEmpty(sysMenuList)) {
            return result;
        }

        for (SysMenu sysMenu : sysMenuList) {
            MenuAndButtonTreeResponse menuAndButtonTreeResponse = new MenuAndButtonTreeResponse();
            menuAndButtonTreeResponse.setId(sysMenu.getMenuId());
            menuAndButtonTreeResponse.setName(sysMenu.getMenuName());
            menuAndButtonTreeResponse.setCode(sysMenu.getMenuCode());
            menuAndButtonTreeResponse.setPid(sysMenu.getMenuParentId());
            menuAndButtonTreeResponse.setChecked(false);

            if (ObjectUtil.isNotEmpty(roleBindMenus)) {
                for (SysRoleMenuDTO roleBindMenu : roleBindMenus) {
                    if (roleBindMenu.getMenuId().equals(sysMenu.getMenuId())) {
                        menuAndButtonTreeResponse.setChecked(true);
                    }
                }
            }

            result.add(menuAndButtonTreeResponse);
        }

        return result;
    }

    /**
     * 菜单集合转化成角色分配菜单的集合
     * <p>
     * 转化过程中包含menu的子集
     *
     * @author fengshuonan
     * @since 2022/9/28 16:42
     */
    public static List<MenuAndButtonTreeResponse> parseMenuAndButtonTreeResponseWithChildren(List<SysMenu> sysMenuList, List<SysRoleMenuDTO> roleBindMenus) {

        // 先转化第一层级的菜单
        List<MenuAndButtonTreeResponse> menuAndButtonTreeResponses = parseMenuAndButtonTreeResponse(sysMenuList, roleBindMenus);

        // 遍历所有菜单，查看是否有二级菜单
        for (SysMenu sysMenu : sysMenuList) {

            // 如果存在二级菜单，则继续进行转化
            if (ObjectUtil.isNotEmpty(sysMenu.getChildren())) {
                for (MenuAndButtonTreeResponse menuAndButtonTreeRespons : menuAndButtonTreeResponses) {
                    if (sysMenu.getMenuId().equals(menuAndButtonTreeRespons.getId())) {
                        List<MenuAndButtonTreeResponse> subLevelItems = parseMenuAndButtonTreeResponse(sysMenu.getChildren(), roleBindMenus);
                        menuAndButtonTreeRespons.setChildren(subLevelItems);
                    }
                }
            }
        }

        return menuAndButtonTreeResponses;
    }

    /**
     * 菜单集合转化成角色分配菜单的集合
     *
     * @author fengshuonan
     * @since 2021/8/10 22:56
     */
    public static void fillButtons(List<MenuAndButtonTreeResponse> sysMenuList, List<SysMenuButton> buttonList, List<SysRoleMenuButtonDTO> roleMenuButtonList) {
        for (MenuAndButtonTreeResponse menuAndButtonTreeResponse : sysMenuList) {
            if (ObjectUtil.isEmpty(buttonList)) {
                continue;
            }

            ArrayList<MenuAndButtonTreeResponse> menuButtonList = new ArrayList<>();

            for (SysMenuButton sysMenuButton : buttonList) {
                if (menuAndButtonTreeResponse.getId().equals(sysMenuButton.getMenuId())) {
                    MenuAndButtonTreeResponse buttonInfo = new MenuAndButtonTreeResponse();
                    buttonInfo.setId(sysMenuButton.getButtonId());
                    buttonInfo.setName(sysMenuButton.getButtonName());
                    buttonInfo.setCode(sysMenuButton.getButtonCode());
                    buttonInfo.setChecked(false);

                    if (ObjectUtil.isNotEmpty(roleMenuButtonList)) {
                        for (SysRoleMenuButtonDTO sysRoleMenuButtonDTO : roleMenuButtonList) {
                            if (sysRoleMenuButtonDTO.getButtonId().equals(sysMenuButton.getButtonId())) {
                                buttonInfo.setChecked(true);
                            }
                        }
                    }

                    menuButtonList.add(buttonInfo);
                }
            }

            menuAndButtonTreeResponse.setButtons(menuButtonList);
        }
    }

    /**
     * 将按钮集成到菜单列表里，并且只返回一级结构
     *
     * @param sysMenuList 菜单集合，包含一级菜单，一级菜单内
     * @param buttonList  菜单下的操作权限集合
     * @author fengshuonan
     * @since 2022/9/28 18:00
     */
    public static List<MenuAndButtonTreeResponse> fillButtons(List<SysMenu> sysMenuList, List<SysMenuButton> buttonList) {
        List<MenuAndButtonTreeResponse> result = new ArrayList<>();

        for (SysMenu sysMenu : sysMenuList) {

            MenuAndButtonTreeResponse menuAndButtonTreeResponse = new MenuAndButtonTreeResponse();
            menuAndButtonTreeResponse.setId(sysMenu.getMenuId());
            menuAndButtonTreeResponse.setName(sysMenu.getMenuName());
            menuAndButtonTreeResponse.setCode(sysMenu.getMenuCode());
            menuAndButtonTreeResponse.setPid(sysMenu.getMenuParentId());
            menuAndButtonTreeResponse.setChecked(false);

            // 获取当前菜单下的所有菜单id
            List<SysMenu> children = sysMenu.getChildren();
            Set<Long> totalMenusIds = children.stream().map(SysMenu::getMenuId).collect(Collectors.toSet());
            totalMenusIds.add(sysMenu.getMenuId());

            // 转化按钮的所属菜单id
            ArrayList<MenuAndButtonTreeResponse> buttons = new ArrayList<>();
            for (SysMenuButton sysMenuButton : buttonList) {
                for (Long menuIdItem : totalMenusIds) {
                    if (sysMenuButton.getMenuId().equals(menuIdItem)) {
                        MenuAndButtonTreeResponse buttonItem = new MenuAndButtonTreeResponse();
                        buttonItem.setId(sysMenuButton.getButtonId());
                        buttonItem.setName(sysMenuButton.getButtonName());
                        buttonItem.setCode(sysMenuButton.getButtonCode());
                        buttonItem.setChecked(false);
                        buttons.add(buttonItem);
                    }
                }
            }
            menuAndButtonTreeResponse.setButtons(buttons);
            result.add(menuAndButtonTreeResponse);
        }

        return result;
    }

    /**
     * 获取分类过的用户菜单，返回一个menus数组，并且第一个是激活的应用
     *
     * @author fengshuonan
     * @since 2021/8/24 16:50
     */
    public static Map<String, List<SysMenu>> sortUserMenusByAppCode(List<SysMenu> currentUserMenus) {

        // 根据应用编码分类的菜单，key是应用编码，value是菜单
        HashMap<String, List<SysMenu>> appMenus = new HashMap<>();

        // 将菜单分类
        for (SysMenu currentUserMenu : currentUserMenus) {

            // 获取这个菜单的应用编码
            String appCode = currentUserMenu.getAppCode();

            // 获取该应用已有的菜单集合
            List<SysMenu> sysMenus = appMenus.get(appCode);
            if (sysMenus == null) {
                sysMenus = new ArrayList<>();
            }

            sysMenus.add(currentUserMenu);
            appMenus.put(appCode, sysMenus);
        }

        return appMenus;
    }

    /**
     * 模型转化
     *
     * @author fengshuonan
     * @since 2021/3/23 21:40
     */
    private static List<AntdSysMenuDTO> doModelTransfer(List<SysMenu> sysMenuList) {
        if (ObjectUtil.isEmpty(sysMenuList)) {
            return null;
        } else {
            ArrayList<AntdSysMenuDTO> resultMenus = new ArrayList<>();

            for (SysMenu sysMenu : sysMenuList) {
                AntdSysMenuDTO antdvMenuItem = new AntdSysMenuDTO();
                antdvMenuItem.setTitle(sysMenu.getMenuName());
                antdvMenuItem.setIcon(sysMenu.getAntdvIcon());
                antdvMenuItem.setPath(sysMenu.getAntdvRouter());
                antdvMenuItem.setComponent(sysMenu.getAntdvComponent());
                antdvMenuItem.setHide(YesOrNotEnum.N.getCode().equals(sysMenu.getAntdvVisible()));
                antdvMenuItem.setActive(sysMenu.getAntdvActiveUrl());
                if (ObjectUtil.isNotEmpty(sysMenu.getChildren())) {
                    antdvMenuItem.setChildren(doModelTransfer(sysMenu.getChildren()));
                }
                resultMenus.add(antdvMenuItem);
            }

            return resultMenus;
        }
    }

    /**
     * 创建顶层应用层级的菜单
     *
     * @author fengshuonan
     * @since 2021/8/24 17:23
     */
    private static AntdSysMenuDTO createRootAppMenu(String appCode) {

        AntdSysMenuDTO antdSysMenuDTO = new AntdSysMenuDTO();

        // 获取应用的详细信息
        AppServiceApi appServiceApi = SpringUtil.getBean(AppServiceApi.class);
        SysAppResult appInfoByAppCode = appServiceApi.getAppInfoByAppCode(appCode);
        antdSysMenuDTO.setTitle(appInfoByAppCode.getAppName());
        antdSysMenuDTO.setIcon(appInfoByAppCode.getAppIcon());
        antdSysMenuDTO.setPath("/" + appCode);
        antdSysMenuDTO.setComponent(null);
        antdSysMenuDTO.setHide(false);
        antdSysMenuDTO.setActive(null);

        return antdSysMenuDTO;
    }

    /**
     * 填充按钮的选中标识
     *
     * @author fengshuonan
     * @since 2022/9/28 18:19
     */
    public static List<MenuAndButtonTreeResponse> fillButtonsChecked(List<MenuAndButtonTreeResponse> menuAndButtonTreeResponses, List<SysRoleMenuButtonDTO> roleMenuButtonList) {

        // 遍历所有菜单中的按钮，将选中标识加上
        for (MenuAndButtonTreeResponse menuAndButtonTreeResponse : menuAndButtonTreeResponses) {

            // 获取菜单中所有的按钮
            List<MenuAndButtonTreeResponse> buttons = menuAndButtonTreeResponse.getButtons();

            // 当前菜单中的所有按钮的数量
            int totalSize = buttons.size();

            // 当前菜单中所有已选择的数量
            int totalSelectSize = 0;

            for (MenuAndButtonTreeResponse button : buttons) {
                for (SysRoleMenuButtonDTO sysRoleMenuButtonDTO : roleMenuButtonList) {
                    if (sysRoleMenuButtonDTO.getButtonId().equals(button.getId())) {
                        button.setChecked(true);
                        totalSelectSize++;
                    }
                }
            }

            // 如果所有按钮都选中行，则设置菜单的选中标识
            if (totalSelectSize == totalSize && totalSelectSize != 0) {
                menuAndButtonTreeResponse.setChecked(true);
            }
        }

        return menuAndButtonTreeResponses;
    }

}
