package com.lantone.security.facade;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.lantone.security.dto.GetMenuByIdDTO;
import com.lantone.security.dto.GetMenusTreeDTO;
import com.lantone.common.enums.StatusEnum;
import com.lantone.common.exception.Asserts;
import com.lantone.common.util.DateUtil;
import com.lantone.common.util.EntityUtil;
import com.lantone.common.util.ListUtil;
import com.lantone.common.util.StringUtil;
import com.lantone.common.util.SysUserUtils;
import com.lantone.security.vo.AddMenuVO;
import com.lantone.security.vo.GetMenusTreeVo;
import com.lantone.security.vo.UpdateMenuVO;
import com.lantone.security.entity.Menu;
import com.lantone.security.entity.MenuResource;
import com.lantone.security.entity.Resource;
import com.lantone.security.entity.RoleSoftwareMenu;
import com.lantone.security.entity.RoleSoftwareResource;
import com.lantone.security.entity.SoftwareMenu;
import com.lantone.security.entity.SoftwareResource;
import com.lantone.security.entity.UserRole;
import com.lantone.security.facade.base.MenuFacade;
import com.lantone.security.facade.base.MenuResourceFacade;
import com.lantone.security.facade.base.ResourceFacade;
import com.lantone.security.facade.base.RoleSoftwareMenuFacade;
import com.lantone.security.facade.base.RoleSoftwareResourceFacade;
import com.lantone.security.facade.base.SoftwareMenuFacade;
import com.lantone.security.facade.base.SoftwareResourceFacade;
import com.lantone.common.enums.IsDeleteEnum;
import com.lantone.security.facade.base.UserRoleFacade;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @ClassName: FuncManagementFacade
 * @Description: 功能管理API
 * @Author songxl
 * @Date 2021/7/28
 * @Version 1.0
 */
@Component
public class FuncManagementFacade {
    @Autowired
    private RoleSoftwareMenuFacade roleSoftwareMenuFacade;
    @Autowired
    private MenuFacade menuFacade;
    @Autowired
    private ResourceFacade resourceFacade;
    @Autowired
    private MenuResourceFacade menuResourceFacade;
    @Autowired
    private SoftwareMenuFacade softwareMenuFacade;
    @Autowired
    private SoftwareResourceFacade softwareResourceFacade;
    @Autowired
    private RoleSoftwareResourceFacade roleSoftwareResourceFacade;
    @Autowired
    private UserRoleFacade userRoleFacade;

    /**
     * @Description获取功能菜单树
     * @Return java.util.List<com.lantone.security.dto.GetMenusTreeDTO>
     */
    public List<GetMenusTreeDTO> getMenusTree() {
        List<GetMenusTreeDTO> menuList = null;
        //2.1获取菜单列表
        GetMenusTreeVo getMenusTreeVo = new GetMenusTreeVo();
        getMenusTreeVo.setCreator(SysUserUtils.getCurrentPrincipleId());
        List<Long> roleIds = userRoleFacade.list(new QueryWrapper<UserRole>()
                .eq("user_id", SysUserUtils.getCurrentPrincipleId())
                .eq("is_deleted", IsDeleteEnum.N.getKey())).stream().map(UserRole::getRoleId).collect(Collectors.toList());
        if (ListUtil.isEmpty(roleIds)) {
            Asserts.fail("当前登录用户没有角色，请联系管理员~");
        }
        if (SysUserUtils.getCurrentSoftwareId() == null) {
            Asserts.fail("请求头服务id为空");
        }
        getMenusTreeVo.setSoftwareId(SysUserUtils.getCurrentSoftwareId());
        getMenusTreeVo.setRoles(roleIds);
        menuList = roleSoftwareMenuFacade.getBaseMapper().getMenus(getMenusTreeVo);
        //3.整理菜单
        if (ListUtil.isNotEmpty(menuList)) {
            Map<Long, List<GetMenusTreeDTO>> menuMap = EntityUtil.makeEntityListMap(menuList, "parentId");
            List<GetMenusTreeDTO> menuRes = menuMap.get(0L);
            for (GetMenusTreeDTO bean : menuRes) {
                getSonMenu(bean, menuMap);
            }
            return menuRes;
        } else {
            Asserts.fail("菜单列表为空");
        }
        return null;
    }


    /**
     * 递归获取菜单结构
     *
     * @param menu    当前菜单
     * @param menuMap 菜单集
     * @return 菜单结构
     */
    public List<GetMenusTreeDTO> getSonMenu(GetMenusTreeDTO menu, Map<Long, List<GetMenusTreeDTO>> menuMap) {
        List<GetMenusTreeDTO> sonMenu = new ArrayList<>();
        List<GetMenusTreeDTO> menuList = menuMap.get(menu.getId());
        if (ListUtil.isNotEmpty(menuList)) {
            menu.setChildren(menuList);
            for (GetMenusTreeDTO bean : menuList) {
                getSonMenu(bean, menuMap);
            }
        }
        return sonMenu;
    }

    /**
     * @param addMenuVO
     * @Description添加功能菜单
     * @Return java.lang.Boolean
     */
    public Boolean addMenu(AddMenuVO addMenuVO) {
        check(null, addMenuVO.getCode());
        //1.插入sys_menu
        //1.1插入sys_menu
        Menu menu = new Menu();
        BeanUtils.copyProperties(addMenuVO, menu);
        menu.setCreator(SysUserUtils.getCurrentPrincipleId() + "");
        menu.setGmtCreate(DateUtil.now());
        if (menuFacade.save(menu)) {
            //2.插入功能菜单对应系统对应系统 sys_software_menu和sys_role_software_menu
            if (savaRoleSoftwareMenu(menu.getId(), addMenuVO.getSoftwares(), 1l)) {
                //插入sys_resource、sys_menu_resource、sys_software_resource
                return saveMenuResource(addMenuVO.getPermissions(), addMenuVO.getSoftwares(), menu.getId(), 1l);
            }
        } else {
            Asserts.fail("菜单详情插入失败");
        }
        return false;
    }

    private void check(Long id, String code) {
        List<Menu> menus = menuFacade.list(new QueryWrapper<Menu>()
                .eq("code", code)
                .eq("is_deleted", IsDeleteEnum.N.getKey()));
        if (id == null) {
            if (ListUtil.isNotEmpty(menus)) {
                Asserts.fail("该权限标识已存在，请重新输入");
            }
        } else {
            menus.stream().forEach(menu -> {
                if (!id.equals(menu.getId())) {
                    Asserts.fail("该权限标识已存在，请重新输入");
                }
            });
        }

    }

    /**
     * @param permissions
     * @param softwares
     * @param menuID
     * @Description插入sys_resource、sys_menu_resource
     * @Return boolean
     */
    private boolean saveMenuResource(List<String> permissions, List<Long> softwares, Long menuID, Long roleID) {
        //2.3 判断该功能菜单是否有对应权限
        if (ListUtil.isNotEmpty(permissions)) {
            permissions.stream().forEach(permission -> {
                //权限插入前判断改权限是否存在（比如新增按钮和修改按钮用的一个权限，新增时权限已经添加修改时就不用添加了）
                List<Resource> hasPermissions = resourceFacade.list(new QueryWrapper<Resource>()
                        .select("id")
                        .eq("url", permission)
                        .eq("is_deleted", IsDeleteEnum.N.getKey()));
                if (hasPermissions.size() > 0) {
                    //2.3 获取对应的 resourceID
                    // 插入菜单和功能权限对应关系
                    MenuResource menuResource = new MenuResource();
                    menuResource.setMenuId(menuID);
                    menuResource.setResourceId(hasPermissions.get(0).getId());
                    menuResourceFacade.save(menuResource);
                } else {
                    //2.4 有权限 则插入sys_resource
                    Resource resource = new Resource();
                    resource.setUrl(permission);
                    resource.setGmtCreate(DateUtil.now());
                    if (resourceFacade.save(resource)) {

                        //2.6 插入菜单和功能权限对应关系
                        MenuResource menuResource = new MenuResource();
                        menuResource.setMenuId(menuID);
                        menuResource.setResourceId(resource.getId());
                        if (menuResourceFacade.save(menuResource)) {
                            //2.7 插入系统与功能权限的对应关系
                            softwares.stream().forEach(softwareId -> {
                                SoftwareResource softwareResource = new SoftwareResource();
                                softwareResource.setSoftwareId(softwareId);
                                softwareResource.setResourceId(resource.getId());
                                softwareResourceFacade.save(softwareResource);
                                //插入超级管理员与SoftwareResource关系 sys_role_software_resource
                                RoleSoftwareResource roleSoftwareResource = new RoleSoftwareResource();
                                roleSoftwareResource.setRoleId(roleID);
                                roleSoftwareResource.setSoftwareResourceId(softwareResource.getId());
                                roleSoftwareResourceFacade.save(roleSoftwareResource);
                            });
                        } else {
                            Asserts.fail("菜单与资源权限插入失败");
                        }
                    } else {
                        Asserts.fail("资源权限插入失败");
                    }
                }

            });
            return true;
        }
        return true;
    }

    /**
     * @param menuID
     * @param softwares
     * @param roleID
     * @Description插入超级管理员对应系统 sys_software_menu和sys_role_software_menu
     */
    private boolean savaRoleSoftwareMenu(Long menuID, List<Long> softwares, Long roleID) {
        if (ListUtil.isNotEmpty(softwares)) {
            softwares.stream().forEach(softwareId -> {
                SoftwareMenu softwareMenu = new SoftwareMenu();
                softwareMenu.setMenuId(menuID);
                softwareMenu.setSoftwareId(softwareId);
                if (softwareMenuFacade.save(softwareMenu)) {
                    RoleSoftwareMenu roleSoftwareMenu = new RoleSoftwareMenu();
                    roleSoftwareMenu.setRoleId(roleID);
                    roleSoftwareMenu.setSoftwareMenuId(softwareMenu.getId());
                    if (roleSoftwareMenuFacade.save(roleSoftwareMenu)) {

                    } else {
                        Asserts.fail("角色与服务菜单关系插入失败");
                    }
                } else {
                    Asserts.fail("服务与菜单对应关系插入失败");
                }
            });
            return true;
        }
        return false;

    }


    /**
     * @param id
     * @param status
     * @Description启用禁用功能菜单
     * @Return java.lang.Boolean
     */
    public Boolean disableMenu(Long id, String status) {
        AtomicBoolean out = new AtomicBoolean(false);
        String selectStatus = "0";
        if (StatusEnum.Enable.getKey().equals(status)) {
            selectStatus = StatusEnum.Disable.getKey();
        } else {
            selectStatus = StatusEnum.Enable.getKey();
        }
        //获取功能菜单及其子集
        List<Long> ids = menuFacade.getBaseMapper().getChildMenus(id, selectStatus);
        boolean flag = menuFacade.update(new UpdateWrapper<Menu>()
                .set("status", status)
                .in("id", ids)
                .eq("is_deleted", IsDeleteEnum.N.getKey()));
        return flag;
    }

    /**
     * @param id
     * @Description删除功能菜单
     * @Return java.lang.Boolean
     */
    public Boolean deleteMenu(Long id) {
        //获取功能菜单及其子集
        List<Long> ids = menuFacade.getBaseMapper().getChildMenus(id, StatusEnum.Enable.getKey());
        //1.删除原来menu关联插入的数据
        if (menuFacade.update(new UpdateWrapper<Menu>()
                .set("is_deleted", IsDeleteEnum.Y.getKey())
                .in("id", ids))) {
            return true;
        } else {
            Asserts.fail("菜单详情删除失败");
        }
        return false;
    }

    /**
     * @param updateMenuVO
     * @Description修改功能菜单
     * @Return java.lang.Boolean
     */
    public Boolean updateMenu(UpdateMenuVO updateMenuVO) {
        check(updateMenuVO.getId(), updateMenuVO.getCode());
        //1.删除原来menu关联插入的数据
        if (deleteMenuJoinDate(updateMenuVO.getId(), 1l)) {
            //2.修改sys_menu 菜单表
            Menu menu = new Menu();
            BeanUtils.copyProperties(updateMenuVO, menu);
            if (menuFacade.updateById(menu)) {
                //3.重新插入上面删除的数据
                //3.1插入功能菜单对应系统对应系统 sys_software_menu和sys_role_software_menu
                if (savaRoleSoftwareMenu(updateMenuVO.getId(), updateMenuVO.getSoftwares(), 1l)) {
                    //3.2插入sys_resource、sys_menu_resource、sys_software_resource
                    return saveMenuResource(updateMenuVO.getPermissions(), updateMenuVO.getSoftwares(), updateMenuVO.getId(), 1l);
                }
            }
        }
        return false;
    }

    /**
     * @param menuId
     * @param roleId
     * @Description清除原菜单关联的数据
     * @Return boolean
     */
    private boolean deleteMenuJoinDate(Long menuId, Long roleId) {

        //2.1删除sys_resource、sys_menu_resource原先对应menu的资源权限
        List<MenuResource> menuResources = menuResourceFacade.list(new QueryWrapper<MenuResource>()
                .eq("menu_id", menuId)
                .eq("is_deleted", IsDeleteEnum.N.getKey()));
        //2.1.1有对应的资源权限
        if (ListUtil.isNotEmpty(menuResources)) {
            List<Long> menuResourceIds = menuResources.stream().map(MenuResource::getId).collect(Collectors.toList());
            if (ListUtil.isNotEmpty(menuResourceIds)) {
                //删除 sys_menu_resource
                if (menuResourceFacade.remove(new UpdateWrapper<MenuResource>()
                        .in("id", menuResourceIds)
                        .eq("is_deleted", IsDeleteEnum.N.getKey()))) {
                    List<Long> resourceIds = menuResources.stream().map(MenuResource::getResourceId).collect(Collectors.toList());
                    if (ListUtil.isNotEmpty(resourceIds)) {
                        //删除sys_resource
                        if (resourceFacade.remove(new UpdateWrapper<Resource>()
                                .in("id", resourceIds)
                                .eq("is_deleted", IsDeleteEnum.N.getKey()))) {
                            //删除sys_software_resource
                            if (softwareResourceFacade.remove(new UpdateWrapper<SoftwareResource>()
                                    .in("resource_id", resourceIds)
                                    .eq("is_deleted", IsDeleteEnum.N.getKey()))) {

                            } else {
                                Asserts.fail("系统与功能权限对应关系删除失败");
                            }
                        } else {
                            Asserts.fail("原菜单对应资源详情删除失败");
                        }
                    }
                } else {
                    Asserts.fail("原菜单对应资源关联关系删除失败");
                }
            }
        }
        //2.2删除sys_software_menu
        if (softwareMenuFacade.remove(new UpdateWrapper<SoftwareMenu>()
                .eq("menu_id", menuId)
                .eq("is_deleted", IsDeleteEnum.N.getKey()))) {
            return true;
        } else {
            Asserts.fail("服务菜单关系清除失败");
        }
        //2.2删除超级管理员该菜单的管理sys_software_menu、sys_role_software_menu
        List<Long> softwareMenuIds = softwareMenuFacade.list(new QueryWrapper<SoftwareMenu>()
                .eq("menu_id", menuId)
                .eq("is_deleted", IsDeleteEnum.N.getKey())).stream().map(SoftwareMenu::getId).collect(Collectors.toList());
        if (roleSoftwareMenuFacade.remove(new UpdateWrapper<RoleSoftwareMenu>()
                .in("software_menu_id", softwareMenuIds)
                .eq("is_deleted", IsDeleteEnum.N.getKey()))) {
            if (softwareMenuFacade.remove(new UpdateWrapper<SoftwareMenu>()
                    .eq("menu_id", menuId)
                    .eq("is_deleted", IsDeleteEnum.N.getKey()))) {
                return true;
            } else {
                Asserts.fail("服务菜单关系清除失败");
            }
        } else {
            Asserts.fail("角色对应服务菜单关系清除失败");
        }
        return false;
    }

    /**
     * @param id
     * @Description获取菜单详情
     * @Return com.lantone.security.dto.MenuDTO
     * @Date 2021/7/29
     */
    public GetMenuByIdDTO getMenuById(Long id, Long softwareId) {
        return menuFacade.getBaseMapper().getMenuById(id, softwareId);
    }

    public boolean initResourceRolesMap(){
        resourceFacade.initResourceRolesMap();
        return true;
    }

}