package com.inspect.system.service.impl;

import com.inspect.base.core.utils.StringUtils;
import com.inspect.common.security.utils.SecurityUtils;
import com.inspect.system.base.domain.SysRole;
import com.inspect.system.base.domain.SysUser;
import com.inspect.system.domain.SysMenu;
import com.inspect.system.domain.vo.MetaVo;
import com.inspect.system.domain.vo.RouterVo;
import com.inspect.system.domain.vo.TreeSelect;
import com.inspect.system.mapper.SysMenuMapper;
import com.inspect.system.mapper.SysRoleMapper;
import com.inspect.system.mapper.SysRoleMenuMapper;
import com.inspect.system.service.ISysMenuService;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.stereotype.Service;

@Service
public class SysMenuServiceImpl implements ISysMenuService {
    public static final String PREMISSION_STRING = "perms[\"{0}\"]";
    private final SysMenuMapper menuMapper;
    private final SysRoleMapper roleMapper;
    private final SysRoleMenuMapper roleMenuMapper;

    public SysMenuServiceImpl(SysMenuMapper menuMapper, SysRoleMapper roleMapper, SysRoleMenuMapper roleMenuMapper) {
        this.menuMapper = menuMapper;
        this.roleMapper = roleMapper;
        this.roleMenuMapper = roleMenuMapper;
    }

    public List<SysMenu> selectMenuList(Long userId) {
        return this.selectMenuList(new SysMenu(), userId);
    }

    public List<SysMenu> selectMenuList(SysMenu menu, Long userId) {
        List<SysMenu> menuList;
        if (SysUser.isAdmin(userId)) {
            menuList = this.menuMapper.selectMenuList(menu);
        } else {
            menu.getParams().put("userId", userId);
            menuList = this.menuMapper.selectMenuListByUserId(menu);
        }

        return menuList;
    }

    public List<SysMenu> selectMenuListAndFilter(SysMenu menu, Long userId) {
        List<SysMenu> menuList;
        if (SysUser.isAdmin(userId)) {
            menuList = this.menuMapper.selectMenuListAndFilter(menu);
        } else {
            menu.getParams().put("userId", userId);
            menuList = this.menuMapper.selectMenuListByUserId(menu);
        }

        return menuList;
    }

    public Set<String> selectMenuPermsByUserId(Long userId) {
        List<String> perms = this.menuMapper.selectMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();

        for (String perm : perms) {
            if (StringUtils.isNotEmpty(perm)) {
                permsSet.addAll(Arrays.asList(perm.trim().split(StringUtils.COMMA)));
            }
        }

        return permsSet;
    }

    public List<SysMenu> selectMenuTreeByUserId(Long userId) {
        List<SysMenu> menus;
        if (SecurityUtils.isAdmin(userId)) {
            menus = this.menuMapper.selectMenuTreeAll();
        } else {
            menus = this.menuMapper.selectMenuTreeByUserId(userId);
        }

        return this.getChildPerms(menus, 0);
    }

    public List<Long> selectMenuListByRoleId(Long roleId) {
        SysRole role = this.roleMapper.selectRoleById(roleId);
        return this.menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
    }

    public List<RouterVo> buildMenus(List<SysMenu> menus) {
        List<RouterVo> routers = new LinkedList<>();

        RouterVo router;
        for (Iterator iterator = menus.iterator(); iterator.hasNext(); routers.add(router)) {
            SysMenu menu = (SysMenu) iterator.next();
            router = new RouterVo();
            router.setHidden("1".equals(menu.getVisible()));
            router.setName(this.getRouteName(menu));
            router.setPath(this.getRouterPath(menu));
            router.setComponent(this.getComponent(menu));
            router.setQuery(menu.getQuery());
            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
            List<SysMenu> cMenus = menu.getChildren();
            if (!cMenus.isEmpty() && "M".equals(menu.getMenuType())) {
                router.setAlwaysShow(Boolean.TRUE);
                router.setRedirect("noRedirect");
                router.setChildren(this.buildMenus(cMenus));
            } else {
                ArrayList childrenList;
                RouterVo children;
                if (this.isMenuFrame(menu)) {
                    router.setMeta(null);
                    childrenList = new ArrayList<>();
                    children = new RouterVo();
                    children.setPath(menu.getPath());
                    children.setComponent(menu.getComponent());
                    children.setName(StringUtils.capitalize(menu.getPath()));
                    children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
                    children.setQuery(menu.getQuery());
                    childrenList.add(children);
                    router.setChildren(childrenList);
                } else if (menu.getParentId().intValue() == 0 && this.isInnerLink(menu)) {
                    router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
                    router.setPath("/");
                    childrenList = new ArrayList<>();
                    children = new RouterVo();
                    String routerPath = this.innerLinkReplaceEach(menu.getPath());
                    children.setPath(routerPath);
                    children.setComponent("InnerLink");
                    children.setName(StringUtils.capitalize(routerPath));
                    children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
                    childrenList.add(children);
                    router.setChildren(childrenList);
                }
            }
        }

        return routers;
    }

    public List<SysMenu> buildMenuTree(List<SysMenu> menus) {
        List<SysMenu> returnList = new ArrayList<>();
        List<Long> tempList = new ArrayList<>();
        Iterator<SysMenu> iterator = menus.iterator();

        SysMenu menu;
        while (iterator.hasNext()) {
            menu = iterator.next();
            tempList.add(menu.getMenuId());
        }

        iterator = menus.iterator();

        while (iterator.hasNext()) {
            menu = iterator.next();
            if (!tempList.contains(menu.getParentId())) {
                this.recursionFn(menus, menu);
                ((List) returnList).add(menu);
            }
        }

        if (((List) returnList).isEmpty()) {
            returnList = menus;
        }

        return (List) returnList;
    }

    public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus) {
        List<SysMenu> menuTrees = this.buildMenuTree(menus);
        return (List) menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    public SysMenu selectMenuById(Long menuId) {
        return this.menuMapper.selectMenuById(menuId);
    }

    public boolean hasChildByMenuId(Long menuId) {
        int result = this.menuMapper.hasChildByMenuId(menuId);
        return result > 0;
    }

    public boolean checkMenuExistRole(Long menuId) {
        int result = this.roleMenuMapper.checkMenuExistRole(menuId);
        return result > 0;
    }

    public int insertMenu(SysMenu menu) {
        return this.menuMapper.insertMenu(menu);
    }

    public int updateMenu(SysMenu menu) {
        return this.menuMapper.updateMenu(menu);
    }

    public int deleteMenuById(Long menuId) {
        return this.menuMapper.deleteMenuById(menuId);
    }

    public String checkMenuNameUnique(SysMenu menu) {
        Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
        SysMenu info = this.menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
        return StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue() ? "1" : "0";
    }

    public String getRouteName(SysMenu menu) {
        String routerName = StringUtils.capitalize(menu.getPath());
        if (this.isMenuFrame(menu)) {
            routerName = "";
        }

        return routerName;
    }

    public String getRouterPath(SysMenu menu) {
        String routerPath = menu.getPath();
        if (menu.getParentId().intValue() != 0 && this.isInnerLink(menu)) {
            routerPath = this.innerLinkReplaceEach(routerPath);
        }

        if (0 == menu.getParentId().intValue() && "M".equals(menu.getMenuType()) && "1".equals(menu.getIsFrame())) {
            routerPath = "/" + menu.getPath();
        } else if (this.isMenuFrame(menu)) {
            routerPath = "/";
        }

        return routerPath;
    }

    public String getComponent(SysMenu menu) {
        String component = "Layout";
        if (StringUtils.isNotEmpty(menu.getComponent()) && !this.isMenuFrame(menu)) {
            component = menu.getComponent();
        } else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && this.isInnerLink(menu)) {
            component = "InnerLink";
        } else if (StringUtils.isEmpty(menu.getComponent()) && this.isParentView(menu)) {
            component = "ParentView";
        }

        return component;
    }

    public boolean isMenuFrame(SysMenu menu) {
        return menu.getParentId().intValue() == 0 && "C".equals(menu.getMenuType()) && menu.getIsFrame().equals("1");
    }

    public boolean isInnerLink(SysMenu menu) {
        return menu.getIsFrame().equals("1") && StringUtils.isHttp(menu.getPath());
    }

    public boolean isParentView(SysMenu menu) {
        return menu.getParentId().intValue() != 0 && "M".equals(menu.getMenuType());
    }

    public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) {
        List<SysMenu> returnList = new ArrayList<>();

        for (SysMenu t : list) {
            if (t.getParentId() == (long) parentId) {
                this.recursionFn(list, t);
                returnList.add(t);
            }
        }

        return returnList;
    }

    private void recursionFn(List<SysMenu> list, SysMenu t) {
        List<SysMenu> childList = this.getChildList(list, t);
        t.setChildren(childList);

        for (SysMenu tChild : childList) {
            if (this.hasChild(list, tChild)) {
                this.recursionFn(list, tChild);
            }
        }

    }

    private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
        List<SysMenu> tlist = new ArrayList<>();

        for (SysMenu n : list) {
            if (n.getParentId().longValue() == t.getMenuId().longValue()) {
                tlist.add(n);
            }
        }

        return tlist;
    }

    private boolean hasChild(List<SysMenu> list, SysMenu t) {
        return !this.getChildList(list, t).isEmpty();
    }

    public String innerLinkReplaceEach(String path) {
        return StringUtils.replaceEach(path, new String[]{"http://", "https://"}, new String[]{"", ""});
    }
}
