package cn.service.system.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import cn.constant.HttpConst;
import cn.constant.CommonConst;
import cn.orm.abs.ISqlToyDao;
import cn.orm.abs.ICommonAPI;
import cn.orm.sqltoy.plus.Wrappers;
import cn.orm.sqltoy.plus.query.LambdaQueryWrapper;
import cn.orm.util.builder.BuildWhere;
import cn.pojo.entity.TSysMenu;
import cn.pojo.entity.TSysRoleMenu;
import cn.pojo.entity.TSysRolePerm;
import cn.pojo.vo.MetaVo;
import cn.pojo.vo.RouterVo;
import cn.pojo.vo.TreeSelectVo;
import cn.service.system.IMenuService;
import cn.util.core.IsKit;
import cn.util.core.TypeKit;

import org.apache.commons.lang3.StringUtils;

@Repository("IMenuService")
public class MenuServiceImpl implements IMenuService
{	
	@Autowired
	private ISqlToyDao daoService;
	@Autowired
	private ICommonAPI sysBaseService;

	/**
	 * 判断菜单menuId是否存在菜单子节点
	 * @param menuId 菜单ID
	 * @return 结果
	 */
	@Override
	public Boolean hasChildByMenuId(Long menuId) 
	{
		LambdaQueryWrapper<TSysMenu> wrapper = Wrappers.lambdaWrapper(TSysMenu.class)
				.eq(TSysMenu::getParentId, menuId);
		return (daoService.count(wrapper) > 0);
	}

	/**
	 * 查询菜单menuId是否已经分配，即在使用
	 * @param menuId 菜单ID
	 * @return 结果
	 */
	@Override
	public Boolean checkMenuExistRole(Long menuId) {
		LambdaQueryWrapper<TSysRoleMenu> wrapper = Wrappers.lambdaWrapper(TSysRoleMenu.class)
				.eq(TSysRoleMenu::getMenuId, menuId);
		return (daoService.count(wrapper) > 0);
	}
	/**
	 * 删除菜单menuId
	 * @param menuId 菜单ID
	 * @return 结果
	 */
	@Override
	public Boolean deleteMenu(Long menuId) {
		LambdaQueryWrapper<TSysMenu> wrapper = Wrappers.lambdaWrapper(TSysMenu.class)
				.eq(TSysMenu::getMenuId,menuId);
		return (daoService.delete(wrapper) > 0);
	}

	/**
	 * 校验菜单名称是否唯一 true 唯一
	 * @param menu 菜单信息
	 * @return 结果
	 */
	@Override
	public Boolean checkMenuNameUnique(TSysMenu menu) 
	{
		Integer menuId = IsKit.isNullOrEmpty(menu.getMenuId()) ? -1 : menu.getMenuId();
		//查询同级菜单下，是否存在同名菜单
		LambdaQueryWrapper<TSysMenu> queryWrapper = Wrappers.lambdaWrapper(TSysMenu.class)
				.select(TSysMenu::getMenuId)
				.eq(TSysMenu::getParentId, menu.getParentId())
				.eq(TSysMenu::getMetaTitle, menu.getMetaTitle());
		TSysMenu entity = daoService.findOne(queryWrapper);
		//不存在
		if (IsKit.isNullOrEmpty(entity)) {
			return true;
		}
		//用于更新时检测用，新增肯定不成立
		return (entity.getMenuId() != menuId);
	}

	/**
	 * 根据用户Id查询系统菜单列表
	 * 角色ID 菜单列表
	 */
	@Override
	public List<TSysMenu> selectMenuList(String roleId) {
		return this.selectMenuList(roleId,null);
	}
	/**
	 * 构建前端所需要下拉树结构
	 * @param menus 菜单列表
	 * @return 下拉树结构列表
	 */
	@Override
	public List<TreeSelectVo> buildMenuTreeSelect(List<TSysMenu> menus) {
		List<TSysMenu> menuTrees = _buildMenuTree(menus);
		return menuTrees.stream().map(TreeSelectVo::new).collect(Collectors.toList());
	}

	/**查询系统菜单列表*/
	@Override
	public List<TSysMenu> selectMenuList4Perm(String roleId) 
	{
		return _getMenus(roleId);
	} 
	/**
	 * 根据用户角色ID查询菜单
     * 菜单列表 只返回非隐藏的有效菜单
	 */
	@Override
	public List<TSysMenu> selectPermMenuList(String roleId){
		List<TSysMenu> menus = _getMenus(roleId);
		return _getChildPerms(menus, 0);
	}
	/**
	 * 根据角色ID查询菜单树信息
	 * @param roleId 角色ID
	 * @return 选中菜单列表
	 * 说明：
	 * 1）此方法时针对页面上：设置角色与按钮操作之间的权限时用
	 * 2）可以存在多个，默认选第一个满足条件的。
	 * 3）menu_id 字段值格式:[1,2,3]
	 * TSysRolePerm
	 */
	@Override
	public List<TSysRolePerm> selectRoleMenuListByRoleId(String roleId)
	{
		LambdaQueryWrapper<TSysRolePerm> queryWrapper = Wrappers.lambdaWrapper(TSysRolePerm.class)
				.eq(TSysRolePerm::getRoleId, roleId);
		//return daoService.findOne(queryWrapper);
		return daoService.findList(queryWrapper);
	}

	/** 根据角色ID查询菜单树信息
	 * @param roleId 角色ID
	 *  @return 选中菜单列表
	 */
	@Override
	public List<Integer> selectMenuListByRoleId(String roleId) {
		return _selectMenuListByRoleId(roleId, true);
	}

	@Override
	public List<TSysMenu> selectMenuTreeByRoleId(String roleId){
		return _selectMenuTreeByRoleId(roleId,null);
	}

	/**保留：根据菜单编号获取详细信息*/
	@Override
	public TSysMenu getMenuById(Long menuId) {
		LambdaQueryWrapper<TSysMenu> queryWrapper = Wrappers.lambdaWrapper(TSysMenu.class)
				.eq(TSysMenu::getMenuId, menuId);
		return daoService.findOne(queryWrapper);
	}

	/**
	 * 构建前端路由所需要的菜单
	 * @param menus 菜单列表
	 * @return 路由列表
	 */
	@Override
	public List<RouterVo> buildMenus(List<TSysMenu> menus) 
	{
		List<RouterVo> routers = new LinkedList<RouterVo>();
		for (TSysMenu menu : menus)
		{
			RouterVo router = new RouterVo();
			router.setName(_getRouteName(menu));
			router.setPath(_getRouterPath(menu));
			router.setComponent(_getComponent(menu));
			//设置MetaVo
			router.setMeta(new MetaVo(
					menu.getMetaTitle(),//菜单栏及 tagsView 栏、菜单搜索名称 
					menu.getMetaIsLink() == null ? "":menu.getMetaIsLink(),//是否超链接菜单，开启外链条件，`1、isLink:true 2、链接地址不为空` 
							//StringUtils.equals("1", TypeUtil.objToStr(menu.getMetaIsHide())),
							TypeKit.objToInt(menu.getMetaIsHide())==1,//0显示 1隐藏 /页面true隐藏
							TypeKit.objToInt(menu.getMetaIsKeepAlive())==0,//是否缓存组件状态 0是
							TypeKit.objToInt(menu.getMetaIsAffix())==0,// 是否固定在 tagsView 栏上 0是
							TypeKit.objToInt(menu.getMetaIsFrame())==0,//是否内嵌窗口开启条件`1、isFrame:true 2、链接地址不为空` 0是
							//new String[]{"admin"},赋值给String[]
							IsKit.isNullOrEmpty(menu.getPerms())? CommonConst.DEFAULT_ADMIN:menu.getPerms(),
									menu.getMetaIcon()
					));
			List<TSysMenu> cMenus = menu.getChildren();
			if (!cMenus.isEmpty() && cMenus.size() > 0 && CommonConst.TYPE_DIR.equals(menu.getMenuType())) {
				router.setRedirect("noRedirect");
				router.setChildren(buildMenus(cMenus));
			} 
			else if (_isMeunFrame(menu)) {
				List<RouterVo> childrenList = new ArrayList<RouterVo>();
				RouterVo children = new RouterVo();
				children.setPath(menu.getPath().replace("//","/").replace("//","/"));
				children.setComponent(menu.getComponent());
				children.setName(StringUtils.capitalize(menu.getPath()));
				router.setMeta(new MetaVo(
						menu.getMetaTitle(), 
						menu.getMetaIsLink() == null ? "" :menu.getMetaIsLink(),
								TypeKit.objToInt(menu.getMetaIsHide())==1,//0显示 1隐藏 /页面true隐藏
								TypeKit.objToInt(menu.getMetaIsKeepAlive())==0,
								TypeKit.objToInt(menu.getMetaIsAffix())==0,
								TypeKit.objToInt(menu.getMetaIsFrame())==0,
								//new String[]{"admin"},赋值给String[]
								IsKit.isNullOrEmpty(menu.getPerms())? "admin":menu.getPerms(),
										menu.getMetaIcon()
						)
						);
				childrenList.add(children);
				router.setChildren(childrenList);
			}
			routers.add(router);
		}
		return routers;
	}

	/**查询系统菜单列表. whereMap查询条件。目前支持的查询条件有：
	 * metaTitle、status、menuType*/
	@Override
	public List<TSysMenu> selectMenuList(String roleId, Map<String,Object> whereMap) 
	{
		List<TSysMenu> menuList = null;
		if (sysBaseService.isAdminByRoleId(roleId)) {
			//管理员 查询满足查询条件whereMap的全部记录
			menuList = daoService.findEntity(TSysMenu.class,BuildWhere.queryWrapper(whereMap));
		} 
		else {//非管理员
			menuList = _selectMenuTreeByRoleId(roleId,whereMap);
		}
		return menuList;
	}
	/**
	 * 根据用户角色ID查询菜单
	 * 用户名称 菜单列表
	 */
	private List<TSysMenu> _selectMenuTreeByRoleId(String roleId,Map<String,Object> whereMap)
	{
		List<TSysMenu> menus = null;
		if (sysBaseService.isAdminByRoleId(roleId)) {
			//管理员 查询满足查询条件whereMap的全部记录 隐藏的是否显示？
			menus = daoService.findBySql("sys_queryMenuTreeByRoleId",whereMap,TSysMenu.class);
		} 
		else {//非管理员
			if(IsKit.isNull(whereMap)) {
				whereMap = new HashMap<>();
			}
			whereMap.put("roleId", roleId);
			menus = daoService.findBySql("sys_queryMenuTreeByRoleId_N",whereMap,TSysMenu.class);
		}
		return menus;//return _getChildPerms(menus, 0);
	}
	/**
	 * 根据角色ID查询菜单树信息
	 * @param roleId 角色ID
	 * @param menuCheckStrictly 菜单树选择项是否关联显示
	 * @return 选中菜单列表
	 * 说明：此方法时针对页面上：设置角色与菜单之间的权限时用QueryWrapperTest
	 */
	private List<Integer> _selectMenuListByRoleId(String roleId, boolean menuCheckStrictly){
		Map<String, Object> paramsMap = new HashMap<>();
		paramsMap.put("roleId", roleId);
		List<TSysMenu> result = daoService.findBySql("sys_queryMenuListByRoleId",paramsMap,TSysMenu.class);
		List<Integer> menuId = result.stream()
				.map(p -> p.getMenuId().intValue())
				.collect(Collectors.toList());
		//.stream().distinct().collect(Collectors.toList()); 去重
		return menuId;
	}
	private List<TSysMenu> _getMenus(String roleId)
	{
		List<TSysMenu> menus = null;
		if (sysBaseService.isAdminByRoleId(roleId)) {
			//管理员 查询满足查询条件whereMap的全部记录.隐藏的是否显示？
			LambdaQueryWrapper<TSysMenu> queryWrapper = Wrappers.lambdaWrapper(TSysMenu.class)
					.eq(TSysMenu::getStatus, "0")
					.eq(TSysMenu::getMetaIsHide,"0")
					.in(TSysMenu::getMenuType, TypeKit.strToList("M,C"));
			menus = daoService.findList(queryWrapper);
		} 
		else {//非管理员
			Map<String,Object> whereMap = new HashMap<>();
			whereMap.put("roleId", roleId);
			menus = daoService.findBySql("sys_queryMenu_N",whereMap,TSysMenu.class);
		}
		return menus;
	}
	/**
	 * 构建前端所需要树结构
	 * @param menus 菜单列表
	 * @return 树结构列表
	 * 可参考部门处理类的对应代码
	 */
	private List<TSysMenu> _buildMenuTree(List<TSysMenu> menus) 
	{
		List<TSysMenu> returnList = new ArrayList<TSysMenu>();
		List<Integer> tempList = new ArrayList<Integer>();
		for (TSysMenu menu : menus) {
			tempList.add(menu.getMenuId());
		}
		for (Iterator<TSysMenu> iterator = menus.iterator(); iterator.hasNext(); ) {
			TSysMenu menu = (TSysMenu) iterator.next();
			// 如果是顶级节点, 遍历该父节点的所有子节点
			if (!tempList.contains(menu.getParentId())) {
				_recursionFn(menus, menu);
				returnList.add(menu);
			}
		}
		if (returnList.isEmpty()) {
			returnList = menus;
		}
		return returnList;
	}

	/**
	 * 根据父节点的ID获取所有子节点
	 * @param list     分类表
	 * @param parentId 传入的父节点ID;其中：parentId=0默认为根节点
	 * @return String
	 */
	private List<TSysMenu> _getChildPerms(List<TSysMenu> list, Integer parentId)
	{
		List<TSysMenu> returnList = new ArrayList<TSysMenu>();
		for (Iterator<TSysMenu> iterator = list.iterator(); iterator.hasNext(); )
		{
			TSysMenu t = (TSysMenu) iterator.next();
			// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点.intValue()
			if (t.getParentId().intValue() == parentId.intValue()) {
				_recursionFn(list, t);
				returnList.add(t);
			}
			else//2022.10.24
				returnList.add(t);
		}
		return returnList;
	}
	/**递归列表*/
	private void _recursionFn(List<TSysMenu> list, TSysMenu t) 
	{
		//得到子节点列表
		List<TSysMenu> childList = _getChildList(list, t);
		t.setChildren(childList);		
		for (TSysMenu tChild : childList) {
			if (_hasChild(list, tChild)) {
				// 判断是否有子节点
				Iterator<TSysMenu> it = childList.iterator();
				while (it.hasNext()) {
					TSysMenu n = (TSysMenu) it.next();
					_recursionFn(list, n);
				}
			}
		}
	}
	/**判断是否有子节点 */
	private boolean _hasChild(List<TSysMenu> list, TSysMenu t) {
		return _getChildList(list, t).size() > 0 ? true : false;
	}
	/**得到子节点列表.t某个节点，list全部节点*/
	private List<TSysMenu> _getChildList(List<TSysMenu> list, TSysMenu t) {
		List<TSysMenu> tlist = new ArrayList<TSysMenu>();
		Iterator<TSysMenu> it = list.iterator();
		while (it.hasNext()) {
			TSysMenu n = (TSysMenu) it.next();
			if (n.getParentId().intValue() == t.getMenuId().intValue()) {
				tlist.add(n);
			}
		}
		return tlist;
	}

	/**
	 * 获取组件信息
	 *
	 * @param menu 菜单信息
	 * @return 组件信息
	 */
	private String _getComponent(TSysMenu menu) {
		String component = CommonConst.LAYOUT;
		if (StringUtils.isNotEmpty(menu.getComponent()) && !_isMeunFrame(menu)) {
			component = menu.getComponent();
		}
		return component;
	}
	/**
	 * 获取路由名称
	 *
	 * @param menu 菜单信息
	 * @return 路由名称
	 */
	private String _getRouteName(TSysMenu menu) {
		String routerName = StringUtils.capitalize(menu.getPath());
		// 非外链并且是一级目录（类型为目录）
		if (_isMeunFrame(menu)) {
			routerName = StringUtils.EMPTY;
		}
		return routerName;
	}

	/**
	 * 获取路由地址
	 * @param menu 菜单信息
	 * @return 路由地址
	 */
	private String _getRouterPath(TSysMenu menu) {
		String routerPath = menu.getPath();
		// 内链打开外网方式
		if (menu.getParentId().intValue() != 0 && _isInnerLink(menu)) {
			routerPath = StringUtils.replaceEach(routerPath, new String[]{
					HttpConst.HTTP, HttpConst.HTTPS}, new String[]{"", ""});
		}
		// 非外链并且是一级目录（类型为目录）
		if (0 == menu.getParentId().intValue() && CommonConst.TYPE_DIR.equals(menu.getMenuType())
				&& CommonConst.INT_NUM_1.equals(menu.getMetaIsFrame())) {
			routerPath = "/" + menu.getPath();
		}
		// 非外链并且是一级目录（类型为菜单）
		else if (_isMenuFrame(menu)) {
			routerPath = "/";
		}
		routerPath = routerPath.replace("//", "/").replace("//", "/");
		return routerPath;
	}

	/**
	 * 是否为菜单内部跳转
	 *
	 * @param menu 菜单信息
	 * @return 结果
	 */
	private boolean _isMeunFrame(TSysMenu menu) {
		return menu.getParentId().intValue() == 0 
				&& CommonConst.TYPE_MENU.equals(menu.getMenuType())
				&& menu.getMetaIsFrame().equals(CommonConst.INT_NUM_1);
	}
	/**
	 * 是否为菜单内部跳转
	 *
	 * @param menu 菜单信息
	 * @return 结果
	 */
	private boolean _isMenuFrame(TSysMenu menu) {
		return menu.getParentId().intValue() == 0 && CommonConst.TYPE_MENU.equals(menu.getMenuType())
				&& menu.getMetaIsFrame().equals(CommonConst.INT_NUM_1);
	}

	/**
	 * 是否为内链组件	 *
	 * @param menu 菜单信息
	 * @return 结果
	 */
	private boolean _isInnerLink(TSysMenu menu) {
		return menu.getMetaIsFrame().equals(CommonConst.INT_NUM_1) && IsKit.ishttp(menu.getPath());
	}
}

