package com.artfess.sysConfig.persistence.manager.impl;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import com.artfess.base.constants.SystemConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.artfess.base.cache.annotation.CacheEvict;
import com.artfess.base.cache.annotation.Cacheable;
import com.artfess.base.conf.SaaSConfig;
import com.artfess.base.constants.TenantConstant;
import com.artfess.base.context.BaseContext;
import com.artfess.base.exception.BaseException;
import com.artfess.base.exception.ServerRejectException;
import com.artfess.base.feign.UCFeignService;
import com.artfess.base.handler.MultiTenantHandler;
import com.artfess.base.handler.MultiTenantIgnoreResult;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.constants.CacheKeyConst;
import com.artfess.base.util.FileUtil;
import com.artfess.base.util.JsonUtil;
import com.artfess.base.util.StringUtil;
import com.artfess.base.util.UniqueIdUtil;
import com.artfess.sysConfig.persistence.dao.SysMenuDao;
import com.artfess.sysConfig.persistence.dao.SysMethodDao;
import com.artfess.sysConfig.persistence.manager.SysMenuManager;
import com.artfess.sysConfig.persistence.model.SysMenu;
import com.artfess.sysConfig.persistence.model.SysMethod;
import com.artfess.uc.api.impl.util.ContextUtil;
import com.artfess.uc.api.model.IUser;

/**
 * 
 * <pre>
 *  
 * 描述：系统菜单 处理实现类
 * 构建组：x6
 * 作者:liyg
 * 邮箱:liygui@jee-soft.cn
 * 日期:2018-06-29 09:34:15
 * 版权：广州宏天软件有限公司
 * </pre>
 */
@Service("sysMenuManager")
public class SysMenuManagerImpl extends BaseManagerImpl<SysMenuDao, SysMenu> implements SysMenuManager {
	@Resource
	SysMethodDao sysMethodDao;
	@Resource
	UCFeignService uCFeignService;
	@Value("${system.mode.demo:false}")
	protected boolean demoMode;
	@Autowired
	SaaSConfig saaSConfig;
	@Resource
	MultiTenantHandler multiTenantHandler;
	@Resource
	BaseContext baseContext;

	/**
	 * 根据别名获取菜单详情 包含请求的方法
	 */
	@Override
	public SysMenu getByAlias(String alias) {
        SysMenu sysMenu = null;
        try(MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()){
            sysMenu = baseMapper.getByAlias(alias);
            if (BeanUtils.isNotEmpty(sysMenu)) {
                List<SysMethod> sysMethods = sysMethodDao.getByMenuAlias(sysMenu.getAlias());
                if (BeanUtils.isNotEmpty(sysMethods)) {
                    sysMenu.setSysMethods(sysMethods);
                }
            }
            return sysMenu;
        }catch (Exception el){
            el.printStackTrace();
        }
        return sysMenu;
	}

	/**
	 * 获取菜单详情 包含请求的方法
	 */
	@Override
	public SysMenu get(Serializable entityId) {
		SysMenu sysMenu = super.get(entityId);
		if (BeanUtils.isNotEmpty(sysMenu)) {
			List<SysMethod> sysMethods = sysMethodDao.getByMenuAlias(sysMenu.getAlias());
			if (BeanUtils.isNotEmpty(sysMethods)) {
				sysMenu.setSysMethods(sysMethods);
			}
		}
		return sysMenu;
	}

	/**
	 * 添加菜单时 同时添加权限方法方法
	 */
	@Override
	public void create(SysMenu entity) {
		super.create(entity);

		sysMethodDao.removeByMenuId(entity.getId());
		List<SysMethod> sysMethods = entity.getSysMethods();
		if (BeanUtils.isNotEmpty(sysMethods)) {
			for (SysMethod sysMethod : sysMethods) {
				sysMethod.setMenuAlias(entity.getAlias());
				sysMethod.setPath(entity.getPath());
				sysMethod.setId(UniqueIdUtil.getSuid());
				sysMethodDao.insert(sysMethod);
			}
		}
		removeUserMenuCache();
	}

	@Override
	public void update(SysMenu entity) {
		if (demoMode) {
			throw new ServerRejectException("演示模式下无法执行该操作");
		}
		super.update(entity);
		sysMethodDao.removeByMenuId(entity.getId());
		List<SysMethod> sysMethods = entity.getSysMethods();
		if (BeanUtils.isNotEmpty(sysMethods)) {
			for (SysMethod sysMethod : sysMethods) {
				sysMethod.setMenuAlias(entity.getAlias());
				sysMethod.setPath(entity.getPath());
				sysMethod.setId(UniqueIdUtil.getSuid());
				sysMethodDao.insert(sysMethod);
			}

		}
		removeUserMenuCache();
	}

	/**
	 * 删除菜单时 同时删除方法请求 1. 先删除从表（sys_method）的数据 从表的数据是根据主表的数据获取别名删除的 2.
	 * 再删除portal_sys_menu数据
	 */
	@Override
	public void remove(Serializable entityId) {
		if (demoMode) {
			throw new ServerRejectException("演示模式下无法执行该操作");
		}
		sysMethodDao.removeByMenuId(entityId);
		super.remove(entityId);
	}

	@Override
	public boolean isExistByAlias(String alias) {
		boolean isInPlateform = false;
		//在租户模式下，当非平台租户添加菜单时，如果别名在平台租户中已存在，则不允许添加（因为平台租户下的菜单是多租户公用的）
		if(saaSConfig.isEnable() && !"-1".equals(baseContext.getCurrentTenantId())){
			List<String> tenantIds = new ArrayList<>();
			tenantIds.add(baseContext.getCurrentTenantId());
			tenantIds.add("-1");
			try(MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()){
				isInPlateform = baseMapper.isExistPlateformByAlias(alias,tenantIds)>0;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return baseMapper.isExistByAlias(alias) > 0 || isInPlateform;
	}

	@Override
	@Transactional
	public void removeByResId(String resId) {
		if (demoMode) {
			throw new ServerRejectException("演示模式下无法执行该操作");
		}
		List<SysMenu> list = getRecursionById(resId);
		for (SysMenu resource : list) {
			this.remove(resource.getId());
		}
		removeUserMenuCache();
	}

	private List<SysMenu> getRecursionById(String resId) {
		List<SysMenu> list = new ArrayList<SysMenu>();

		SysMenu resource = this.get(resId);
		list.add(resource);

		List<SysMenu> tmpList = baseMapper.getByParentId(resId);
		if (BeanUtils.isEmpty(tmpList))
			return list;

		for (SysMenu sysMenu : tmpList) {
			recursion(sysMenu, list);
		}
		return list;
	}

	private void recursion(SysMenu sysMenu, List<SysMenu> list) {
		list.add(sysMenu);
		List<SysMenu> tmpList = baseMapper.getByParentId(sysMenu.getId());
		if (BeanUtils.isEmpty(tmpList))
			return;

		for (SysMenu resource : tmpList) {
			recursion(resource, list);
		}
	}
	
	@Cacheable(value = CacheKeyConst.EIP_SYS_USERMENU, key="#currentUser.userId")
	protected String getMenuByUserId(IUser currentUser) throws IOException{
        List<SysMenu> dbMenus = null;

        if(currentUser.isAdmin()){
            dbMenus = this.getAllByTenant(null,"1");
        }

        if( BeanUtils.isEmpty(dbMenus) ){
        	// 1.获取用户的所有角色
            List<ObjectNode>  roles =  uCFeignService.getRoleListByAccount(currentUser.getAccount());
            List<String> roleCodes = new ArrayList<String>();
            if(BeanUtils.isNotEmpty(roles)){
            	// 2.构建角色别名列表
                for (ObjectNode role : roles) {
                    if (1 == role.get("enabled").asInt()) {
                        roleCodes.add(role.get("code").asText());
                    }
                }
                if (BeanUtils.isNotEmpty(roleCodes)) {
                	String tenantId = saaSConfig.isEnable()?baseContext.getCurrentTenantId():null;
                	try(MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()){
                		List<String> ignoreMenus = null;
                		// 3.是否需要添加忽略的菜单（非平台租户需要忽略一些菜单目录）
                		if(saaSConfig.isEnable() && !TenantConstant.PLATFORM_TENANT_ID.equals(currentUser.getTenantId())){
                			ignoreMenus = uCFeignService.getIgnoreMenuCodes(currentUser.getTenantId());
                		}
                		// 4.根据角色列表查询角色对应的菜单目录
                		dbMenus = baseMapper.getMenuByRoleAlias(roleCodes, tenantId, BeanUtils.isEmpty(ignoreMenus)?null:ignoreMenus);
                	}
                	catch(Exception e) {
                		throw new BaseException(e);
                	}
                }
            }
        }
        if(BeanUtils.isEmpty(dbMenus)) {
            dbMenus= new ArrayList<SysMenu>();
        }
        unique(dbMenus);
        String json = JsonUtil.toJson(dbMenus);
        return json;
	}

	@Override
	public List<SysMenu> getCurrentUserMenu() throws Exception {
		IUser currentUser = ContextUtil.getCurrentUser();
		if(BeanUtils.isEmpty(currentUser)) {
			throw new BaseException("未获取到当前登录用户");
		}
		SysMenuManagerImpl bean = AppUtil.getBean(getClass());
		String menuByUserId = bean.getMenuByUserId(currentUser);
		List<SysMenu> list = new ArrayList<>();
		if(menuByUserId!=null) {
			list = JsonUtil.toBean(menuByUserId, new TypeReference<List<SysMenu>>(){});
		}
		return list;
	}

	@Override
	public List<SysMenu> getMenuByRole(String roleCodes) {
		IUser currentUser = ContextUtil.getCurrentUser();
		List<SysMenu> dbMenus = new ArrayList<>();
		if (BeanUtils.isNotEmpty(roleCodes)) {
			String tenantId = saaSConfig.isEnable()?baseContext.getCurrentTenantId():null;
			try(MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()){
				List<String> ignoreMenus = null;
				// 3.是否需要添加忽略的菜单（非平台租户需要忽略一些菜单目录）
				if(saaSConfig.isEnable() && !TenantConstant.PLATFORM_TENANT_ID.equals(currentUser.getTenantId())){
					ignoreMenus = uCFeignService.getIgnoreMenuCodes(currentUser.getTenantId());
				}
				// 4.根据角色列表查询角色对应的菜单目录
				List<String> roleCode =  Arrays.asList(roleCodes.split(","));
				dbMenus = baseMapper.getMenuByRoleAlias(roleCode,tenantId,BeanUtils.isEmpty(ignoreMenus)?null:ignoreMenus);
			}catch(Exception e) {
				throw new BaseException(e);
			}
		}
		return dbMenus;
	}

	@Override
	public List<Map<String, Object>> getAllMenuRoleAlias(String roleAlias,String status) {
		QueryWrapper<SysMenu> queryWrapper = getTenantQueryWrapper();
		List<Map<String, Object>> result = null;
		String tenantId = saaSConfig.isEnable()?baseContext.getCurrentTenantId():null;
		try(MultiTenantIgnoreResult multiTenantIgnoreResult = MultiTenantHandler.setThreadLocalIgnore()){
			queryWrapper.eq(StringUtil.isNotEmpty(status),"STATUS_", "1");
			result = baseMapper.getAllMenuRoleAlias(roleAlias, tenantId, queryWrapper);
		}
		catch(Exception e) {
			throw new BaseException(e.getMessage(),e);
		}
		return result;
	}

	// 去除列表中的重复项
	private void unique(List<SysMenu> objects) {
		for (int i = 0; i < objects.size() - 1; i++) {
			for (int j = objects.size() - 1; j > i; j--) {
				if (objects.get(j).getId().equals(objects.get(i).getId())) {
					objects.remove(j);
				}
			}
		}
	}

	@Override
	public List<SysMenu> filterByMenuAlias(String menuAlias, List<SysMenu> lists) {
        List<SysMenu> result = new ArrayList<SysMenu>();
        if(BeanUtils.isEmpty(lists) || StringUtil.isEmpty(menuAlias)) {
            return result;
        }
        String pId = null;
        // 获取菜单别名对应id
        for(SysMenu sysMenu : lists) {
            if(menuAlias.equals(sysMenu.getAlias())) {
                pId = sysMenu.getId();
                break;
            }
        }
        if(StringUtil.isEmpty(pId)) {
            return result;
        }
        // 通过parentId来过滤菜单数组
        BeanUtils.listByPid(lists, pId, result);
        // 将返回的数组由平铺结果转换为nest结构
        return BeanUtils.listToTree(result);
	}

	@Override
	public List<SysMenu> getMenuByRoleAlias(String roleAlias) {
		return baseMapper.getMenuByRoleAlias(Arrays.asList(roleAlias.split(",")), null, null);
	}

	@Override
	public List<SysMenu> getAllByTenant(String ignoreAlias,String status) {
		List<SysMenu> dbMenus = new ArrayList<SysMenu>();
		QueryWrapper<SysMenu>  queryWrapper = getTenantQueryWrapper();
		//处理忽略菜单
		dealWithIgnoreQuery(queryWrapper, ignoreAlias);
        try(MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()){
			queryWrapper.eq(StringUtil.isNotEmpty(status),"STATUS_", "1");
        	dbMenus = this.list(queryWrapper);
        }
        catch(Exception e) {
        	throw new BaseException(e.getMessage(),e);
        }
		return dbMenus;
	}
	
	/**
	 * 处理忽略菜单以及下级菜单
	 * @param queryWrapper
	 * @param ignoreAlias
	 */
	private void dealWithIgnoreQuery(QueryWrapper<SysMenu>  queryWrapper,String ignoreAlias){
		if(StringUtil.isNotEmpty(ignoreAlias)){
			String[] ignores = ignoreAlias.split(",");
			List<String> allIgnores = new ArrayList<>();
			try(MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()){
				for (String alias : ignores) {
					SysMenu menu = baseMapper.getByAlias(alias);
					if(BeanUtils.isNotEmpty(menu)){
						allIgnores.add(alias);
						List<SysMenu> childrens = null;
						childrens = baseMapper.getByChidrensParentPath(menu.getPath());
						if(BeanUtils.isNotEmpty(childrens)){
							for (SysMenu sysMenu : childrens) {
								allIgnores.add(sysMenu.getAlias());
							}
						}
					}
					
				}
			}
			catch(Exception e) {
				throw new BaseException(e.getMessage(),e);
			}
			if(BeanUtils.isNotEmpty(allIgnores)){
				BeanUtils.removeDuplicate(allIgnores);
				allIgnores.addAll(TenantConstant.IGNORE_MENU);
				queryWrapper.and(consumer -> {
					consumer.notIn("alias_", allIgnores);
				});
			}
		}
	}

	private QueryWrapper<SysMenu> getTenantQueryWrapper() {
		IUser currentUser = ContextUtil.getCurrentUser();
		QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<SysMenu>();
		if (saaSConfig.isEnable()) {
			queryWrapper.or().in(multiTenantHandler.getTenantIdColumn(),
					Arrays.asList(TenantConstant.PLATFORM_TENANT_ID, currentUser.getTenantId()));
			if (!TenantConstant.PLATFORM_TENANT_ID.equals(currentUser.getTenantId())) {
				queryWrapper.and(consumer -> {
					List<String> allIgnoreMenus = new ArrayList<String>();
					allIgnoreMenus.addAll(TenantConstant.IGNORE_MENU);
					try {
						List<String> ignoreMenus = uCFeignService.getIgnoreMenuCodes(currentUser.getTenantId());
						if (BeanUtils.isNotEmpty(ignoreMenus)) {
							allIgnoreMenus.addAll(ignoreMenus);
						}
					} catch (Exception e) {
						throw new BaseException("获取当前租户禁用菜单出错：" + e.getMessage(),e);
					}
					consumer.notIn("alias_", allIgnoreMenus);
				});

			}
		}else{
			//当为非租户模式时，不显示租户相关菜单
			queryWrapper.notIn("alias_", "tenant", "tenantManager", "tenantTypeManager", "tenantParamsManager");
		}
		queryWrapper.orderByAsc("sn_");
		return queryWrapper;
	}
	
	private void removeUserMenuCache() {
		SysMenuManagerImpl bean = AppUtil.getBean(getClass());
		bean.delUserMenuCache();
	}

	@Override
	@CacheEvict(value = CacheKeyConst.EIP_SYS_USERMENU, allEntries = true)
	public void delUserMenuCache() {}
	
	@Override
	public List<SysMenu> getByChidrensParentPath(String path) {
		return baseMapper.getByChidrensParentPath(path);
	}

	@Override
	@Transactional
	public void importFile(String unZipFilePath, String parentMenuId) {
        try{
        	
        	SysMenu baseParentMenu = get(parentMenuId);
        	String parentJson = FileUtil.readFile(unZipFilePath + File.separator + "parentMenu.json");
        	SysMenu psysMenu = JsonUtil.toBean(parentJson, SysMenu.class);
        	String npMenuId = UniqueIdUtil.getSuid();
        	String basePath = baseParentMenu.getPath()+npMenuId+".";
            String json = FileUtil.readFile(unZipFilePath + File.separator + "sysMenus.json");
            List list = JsonUtil.toBean(json, List.class);
            List<SysMenu> newMenus = new ArrayList<>();
            Map<String,String> idsMap = new HashMap<String, String>();
            for (Object o : list) {
            	SysMenu sysMenu = JsonUtil.toBean(JsonUtil.toJson(o), SysMenu.class);
            	SysMenu menu = this.getByAlias(sysMenu.getAlias());
            	if(BeanUtils.isNotEmpty(menu)){
            		throw new BaseException("导入菜单中某些别名已存在。");
            	}
               if(sysMenu.getId().equals(psysMenu.getId())){
            	   sysMenu.setParentId(parentMenuId);
            	   sysMenu.setPath(basePath);
               }else{
            	   String nId = UniqueIdUtil.getSuid();
            	   idsMap.put(sysMenu.getId(), nId);
            	   String[] cpath = sysMenu.getPath().split("."+psysMenu.getId()+".");
            	   String suffix = cpath[1].replace(sysMenu.getId()+".", nId+".");
            	   sysMenu.setId(nId);
            	   sysMenu.setPath(basePath+suffix);
               }
               newMenus.add(sysMenu);
            }
            if(BeanUtils.isNotEmpty(newMenus)){
            	String tenantId = baseContext.getCurrentTenantId();
            	try(MultiTenantIgnoreResult setThreadLocalIgnore = MultiTenantHandler.setThreadLocalIgnore()){
            		for (SysMenu sysMenu : newMenus) {
            			if(idsMap.containsKey(sysMenu.getParentId())){
            				sysMenu.setParentId(idsMap.get(sysMenu.getParentId()));
            			}
            			sysMenu.setTenantId(tenantId);
                		this.create(sysMenu);
    				}
    			}
    			catch(Exception e) {
    				throw new BaseException(e.getMessage(),e);
    			}
            }
        }catch (Exception e){
            throw new BaseException(e.getMessage());
        }
	}

	@Override
	public Map<String,List<SysMenu>> getThreeAdminMenu(String tenantId) throws Exception {
		Map<String,List<SysMenu>> map = new HashMap<>();
		List<String> ignoreMenus = null;
		// 1.是否需要添加忽略的菜单（非平台租户需要忽略一些菜单目录）
		if(saaSConfig.isEnable() && !TenantConstant.PLATFORM_TENANT_ID.equals(tenantId)){
			ignoreMenus = uCFeignService.getIgnoreMenuCodes(tenantId);
		}
		// 2.根据系统管理员角色列表查询角色对应的菜单目录
		List<String> roleCode = new ArrayList<>();
		roleCode.add(SystemConstants.SYSTEM_ROLE);
		List<SysMenu> systemRoleMenus = baseMapper.getMenuByRoleAlias(roleCode, tenantId, BeanUtils.isEmpty(ignoreMenus)?null:ignoreMenus);
		map.put(SystemConstants.SYS_ADMIN_ACCOUNT,systemRoleMenus);
		// 3.根据安全管理员角色列表查询角色对应的菜单目录
		roleCode.clear();
		roleCode.add(SystemConstants.SEC_ROLE);
		List<SysMenu> secRoleMenus = baseMapper.getMenuByRoleAlias(roleCode, tenantId, BeanUtils.isEmpty(ignoreMenus)?null:ignoreMenus);
		map.put(SystemConstants.SEC_ACCOUNT,secRoleMenus);
		// 4.根据日志审计员角色列表查询角色对应的菜单目录
		roleCode.clear();
		roleCode.add(SystemConstants.AUDIT_ROLE);
		List<SysMenu> auditRoleMenus = baseMapper.getMenuByRoleAlias(roleCode, tenantId, BeanUtils.isEmpty(ignoreMenus)?null:ignoreMenus);
		map.put(SystemConstants.AUDIT_ACCOUNT,auditRoleMenus);
		return map;
	}
}
