package com.sdtool.business.service;

import com.sdtool.business.dao.SystemDao;
import com.sdtool.common.api.NoSqlKeysService;
import com.sdtool.common.api.PermissionService;
import com.sdtool.common.api.RoleService;
import com.sdtool.common.base.BaseService;
import com.sdtool.common.datas.RedisKeys;
import com.sdtool.common.entity.base.ZTreeData;
import com.sdtool.common.entity.site.ERPTokenUser;
import com.sdtool.common.entity.system.Menu;
import com.sdtool.common.entity.system.Role;
import com.sdtool.common.entity.system.RoleMenu;
import com.sdtool.common.entity.system.UserRole;
import com.dySweetFishPlugin.redis.RedisService;
import com.dySweetFishPlugin.sql.TableIdService;
import com.dySweetFishPlugin.sql.dao.TunaService;
import com.sweetfish.convert.json.JsonConvert;
import com.sweetfish.service.Local;
import com.sweetfish.service.RetResult;
import com.sweetfish.source.PageFlipper;
import com.sweetfish.util.AnyValue;
import com.sweetfish.util.AutoLoad;
import com.sweetfish.util.ResourceType;
import org.apache.commons.lang3.StringUtils;
import org.rex.RMap;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@AutoLoad(false)
@Local
@ResourceType(RoleService.class)
public class RoleServiceImpl extends BaseService implements RoleService {

    @Resource
    RedisService redisService;

    @Resource
    NoSqlKeysService keysService;

    @Resource
    JsonConvert jsonConvert;

    @Resource
    TableIdService tableIdService;

    @Resource
    TunaService tunaService;

    @Resource
    PermissionService permissionService;

    @Resource(name = "property.menuResourcerRoleIds")
    private long menuResourcerRoleIds;

    private SystemDao systemDao;

    @Override
    public void init(AnyValue config) {
        super.init(config);
    }

    @Override
    public void destroy(AnyValue config) {
        super.destroy(config);
    }

    @Override
    public void start(AnyValue config) {
        systemDao = tunaService.generate(SystemDao.class);
    }

    /**
     * 在redis中构造角色、角色菜单、角色功能等缓存
     * 系统启动的时候做一次，然后修改菜单的时候需要手工进行处理，否则菜单权限不正确
     */

    private Role getRedisRole(long roleId, long supplierCode) {
        String json = redisService.hget(keysService.getRedisKey(RedisKeys.KEY_SYSROLES, supplierCode), String.valueOf(roleId));

        if (StringUtils.isEmpty(json)) {
            return null;
        }

        return jsonConvert.convertFrom(Role.class, json);
    }

    private String getRedisRoleName(long roleId, String dataSourceId, long supplierCode) {
        Role role = getRedisRole(roleId, supplierCode);

        if (role == null) {
            return "";
        } else {
            return role.getRoleName();
        }
    }

    //region 角色管理

    /**
     * 获取角色列表，用于查询、增加、修改
     *
     * @param searchData 查询条件
     * @return 角色列表
     */
    @Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public RetResult<List<Role>> getRoleList(RMap searchData, PageFlipper pageFlipper, String dataSourceId, long supplierCode) {
        RMap params = new RMap();
        if (searchData != null) {
            params.putAll(searchData);
        }

        if (!params.containsKey("status")) {
            params.put("status", -1);
        }

        if (params.containsKey("keyWord")) {
            String keyWord = params.getString("keyWord");
            params.put("roleName", keyWord);
        }

        List<Role> roleList = systemDao.selectRolePage(params, pageFlipper, dataSourceId, supplierCode);
        return RetResult.<List<Role>>successT().result(roleList).page(pageFlipper);
    }

    /**
     * 从redis中获取用户角色列表
     *
     * @param idUser       用户id
     * @param supplierCode 分表
     * @return 用户角色列表
     */
    private List<UserRole> getRedisUserRole(long idUser, long supplierCode) {
        if (idUser == -999L) {
            UserRole userRole = new UserRole();
            userRole.setRoleId(menuResourcerRoleIds);
            userRole.setUserId(-999L);
            List<UserRole> list = new ArrayList<>();
            list.add(userRole);
            return list;
        }

        Map<String, String> redisMap = redisService.hgetAll(keysService.getRedisKey(RedisKeys.KEY_SYSUSERROLES, supplierCode, true) + idUser);

        if ((redisMap == null) || (redisMap.isEmpty())) {
            return null;
        }

        return redisMap.values().stream().map((x) -> (UserRole) jsonConvert.convertFromO(UserRole.class, x)).collect(Collectors.toList());
    }

    public void writeRedisUserRole(long idUser, List<UserRole> userRoleList, long supplierCode) {
        //存储到redis中
        Map<String, String> redisMap = userRoleList.stream().collect(
                Collectors.toMap(x -> String.valueOf(x.getRoleId()), x -> jsonConvert.convertTo(x))
        );
        if (!redisMap.isEmpty()) {
            redisService.del(keysService.getRedisKey(RedisKeys.KEY_SYSUSERROLES, supplierCode, true) + idUser);
            redisService.hmset(keysService.getRedisKey(RedisKeys.KEY_SYSUSERROLES, supplierCode, true) + idUser, redisMap);
        }
    }

    /**
     * 获取用户角色列表
     * 先从redis中获取，如果没有，则从数据库中获取，并更新redis
     *
     * @param id           用户id
     * @param dataSourceId 分库
     * @param supplierCode 分表
     * @return 用户角色列表
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public List<UserRole> getUserRoleList(long id, String dataSourceId, long supplierCode) {
        List<UserRole> userRoleList = getRedisUserRole(id, supplierCode);
        if ((userRoleList == null) || (userRoleList.isEmpty())) {
            RMap params = new RMap();
            params.put("userid", id);
            userRoleList = systemDao.queryUserRoles(params, dataSourceId, supplierCode);

            if (userRoleList.isEmpty()) {
                return Collections.emptyList();
            }

            writeRedisUserRole(id, userRoleList, supplierCode);
        }

        return userRoleList;
    }

    private List<RoleMenu> convertRoleMenu(long roleId, String menuIds, boolean onlyMenu, long supplierCode) {
        List<RoleMenu> roleMenuList = new ArrayList<>();

        if (StringUtils.isNotEmpty(menuIds)) {
            String[] menuIdArray = menuIds.split(",");
            List<String> menuRedisList = redisService.hmget(keysService.getRedisKey(RedisKeys.KEY_SYSMENUS, supplierCode), menuIdArray);

            for (String menuStr : menuRedisList) {
                Menu menu = jsonConvert.convertFrom(Menu.class, menuStr);

                boolean bAdd = true;

                if (onlyMenu) {
                    if (menu.getMenuType().equalsIgnoreCase("F")) {
                        bAdd = false;
                    }
                }

                if (bAdd) {
                    RoleMenu roleMenu = new RoleMenu();
                    roleMenu.setRoleId(roleId);
                    roleMenu.setMenuId(menu.getId());
                    roleMenu.setMenuPerms(menu.getPerms());
                    roleMenu.setMenuType(menu.getMenuType());
                    roleMenu.setParentId(menu.getParentId());

                    roleMenuList.add(roleMenu);
                }
            }
        }

        return roleMenuList;
    }


    /**
     * 增加角色
     *
     * @param role        角色信息
     * @param currentUser 操作人
     * @return 操作信息
     */
    @Override
    public RetResult<Role> addRole(Role role, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        role.setId(tableIdService.getTableIdMulti("tbRole.id", 1, dataSourceId, String.valueOf(supplierCode)));
        role.setStatus(0);
        role.setSystemFlag(0);

        Role.create(role, currentUser.getId());

        List<RoleMenu> roleMenuList = convertRoleMenu(role.getId(), role.getMenuIds(), true, supplierCode);
        List<RoleMenu> roleActionList = convertRoleMenu(role.getId(), role.getMenuIds(), false, supplierCode);

        permissionService.writeRedisRoleMenu(role.getId(), roleMenuList, roleActionList, supplierCode);

        //设置redis角色主表信息
        redisService.hset(keysService.getRedisKey(RedisKeys.KEY_SYSROLES, supplierCode), String.valueOf(role.getId()), jsonConvert.convertTo(role));

        systemDao.addRole(role, roleActionList, dataSourceId, supplierCode);

        return RetResult.successT();
    }

    /**
     * 修改角色
     *
     * @param role        角色信息
     * @param currentUser 操作人
     * @return 操作信息
     */
    @Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public RetResult<Role> updateRole(Role role, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        if (role == null) {
            return RetResult.<Role>errorT().retinfo("请求数据错误");
        }

        if (role.getId() <= 0L) {
            return RetResult.<Role>errorT().retinfo("请求数据错误");
        }

        Role.update(role, currentUser.getId());

        Role redisRole = getRedisRole(role.getId(), supplierCode);
        if (redisRole == null) {
            return RetResult.<Role>errorT().retinfo("无效的角色信息");
        }
        if (redisRole.getSystemFlag() == 1) {
            return RetResult.<Role>errorT().retinfo("内置角色不允许修改");
        }

        redisRole.setRoleName(role.getRoleName());
        redisRole.setRemark(role.getRemark());
        redisRole.setSortNum(role.getSortNum());
        redisRole.setUpdateBy(role.getUpdateBy());
        redisRole.setUpdateTime(role.getUpdateTime());

        redisService.hset(keysService.getRedisKey(RedisKeys.KEY_SYSROLES, supplierCode), String.valueOf(redisRole.getId()), jsonConvert.convertTo(redisRole));

        List<RoleMenu> roleMenuList = convertRoleMenu(role.getId(), role.getMenuIds(), true, supplierCode);
        List<RoleMenu> roleActionList = convertRoleMenu(role.getId(), role.getMenuIds(), false, supplierCode);

        permissionService.writeRedisRoleMenu(role.getId(), roleMenuList, roleActionList, supplierCode);

        RMap params = new RMap();
        params.put("roleId", role.getId());

        systemDao.updateRole(role, params, roleActionList, dataSourceId, supplierCode);

        return RetResult.successT();
    }

    /**
     * 删除角色
     *
     * @param role        角色信息
     * @param currentUser 操作人
     * @return 操作信息
     */
    @Override
    public RetResult<Role> removeRole(Role role, ERPTokenUser currentUser, String dataSourceId, long supplierCode) {
        if (role == null) {
            return RetResult.<Role>errorT().retinfo("请求数据错误");
        }

        if (role.getId() <= 0L) {
            return RetResult.<Role>errorT().retinfo("请求数据错误");
        }

        Role.update(role, currentUser.getId());

        Role redisRole = getRedisRole(role.getId(), supplierCode);
        if (redisRole == null) {
            return RetResult.<Role>errorT().retinfo("无效的角色信息");
        }
        if (redisRole.getSystemFlag() == 1) {
            return RetResult.<Role>errorT().retinfo("内置角色不允许修改");
        }
        redisRole.setStatus(role.getStatus());
        redisRole.setUpdateBy(role.getUpdateBy());
        redisRole.setUpdateTime(role.getUpdateTime());

        redisService.hset(keysService.getRedisKey(RedisKeys.KEY_SYSROLES, supplierCode),
                String.valueOf(redisRole.getId()), jsonConvert.convertTo(redisRole));

        systemDao.voidRole(role, dataSourceId, supplierCode);

        return RetResult.successT();
    }

    /**
     * 获取角色对应的菜单树形结构，填充ztree
     *
     * @param roleId 角色id
     * @return 树形角色
     */
    @Override
    public RetResult<List<ZTreeData>> getRoleMenuTreeData(long roleId, String dataSourceId, long supplierCode) {
        Map<String, String> menuMap = redisService.hgetAll(keysService.getRedisKey(RedisKeys.KEY_SYSMENUS, supplierCode));

        Map<String, String> roleMenuMap = redisService.hgetAll(keysService.getRedisKey(RedisKeys.KEY_SYSROLEACTIONS, supplierCode, true) + roleId);
        List<RoleMenu> roleMenuList = new ArrayList<>();
        roleMenuMap.forEach((k, v) -> {
            RoleMenu roleMenu = jsonConvert.convertFrom(RoleMenu.class, v);
            roleMenuList.add(roleMenu);
        });

        List<ZTreeData> menuList = new ArrayList<>();
        menuMap.forEach((k, v) -> {
            Menu menu = jsonConvert.convertFrom(Menu.class, v);
//            if (menu.getVisible() == 1) {   //隐藏让前端判断,因为有可能会存在已隐藏又生效的菜单
            ZTreeData data = new ZTreeData();
            data.setId(menu.getId());
            data.setpId(menu.getParentId());
            data.setSortIndex(menu.getSortNum());
            data.setVisible(menu.getVisible());

            if (StringUtils.isEmpty(menu.getPerms())) {
                data.setName(menu.getMenuName());
            } else {
                data.setName(menu.getMenuName() + "<font color=\"#888\">&nbsp;&nbsp;&nbsp;" + menu.getPerms() + "</font>");
            }
            data.setTitle(menu.getMenuName());

            data.setChecked(roleMenuList
                    .stream()
                    .anyMatch((x) -> (x.getMenuId() == menu.getId())));

            menuList.add(data);
//            }
        });

        menuList.sort(Comparator.comparing(ZTreeData::getSortIndex));
        return RetResult.<List<ZTreeData>>successT().result(menuList);
    }

    /**
     * 获取角色信息
     *
     * @param roleId 角色id
     * @return 角色信息
     */
    @Override
    public RetResult<Role> getRoleInfo(long roleId, long supplierCode) {
        Role role = getRedisRole(roleId, supplierCode);
        return RetResult.<Role>successT().result(role);
    }

    //endregion
}
