package com.changfa.config.shiro;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.changfa.constants.*;
import com.changfa.dto.MenuDto;
import com.changfa.dto.UserBaseDto;
import com.changfa.entity.TRole;
import com.changfa.entity.TUser;
import com.changfa.enums.LoginStatus;
import com.changfa.enums.StatusEnum;
import com.changfa.exception.BuzEx;
import com.changfa.service.ITRoleService;
import com.changfa.service.ITUserService;
import com.changfa.utils.BCrypt;
import com.changfa.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author wwz
 */

@Slf4j
public class CustomRealm extends AuthorizingRealm {


    @Autowired
    private ITUserService userService;
    @Autowired
    private ITRoleService roleService;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * @MethodName doGetAuthorizationInfo
     * @Description 权限配置类
     * @Param [principalCollection]
     * @Return AuthorizationInfo
     * @Author WangShiLin
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("=======================权限配置=========================");
        //获取登录用户名
        String name = (String) principalCollection.getPrimaryPrincipal();
        //查询用户名称
        UserBaseDto userBaseDto = LoginConstant.userBaseInfo();
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        TRole role = userBaseDto.getRole();
        if(role != null){
            simpleAuthorizationInfo.addRole(role.getRoleCode());

        }
        //添加角色和权限

        return simpleAuthorizationInfo;
    }

    /**
     * @MethodName doGetAuthenticationInfo
     * @Description 认证配置类
     * @Param [authenticationToken]
     * @Return AuthenticationInfo
     * @Author wwz
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        if (StringUtils.isEmpty(authenticationToken.getPrincipal())) {
            return null;
        }
        SimpleAuthenticationInfo simpleAuthenticationInfo = null;
                //获取用户信息
        String name = authenticationToken.getPrincipal().toString();
        char[] passwords = (char[]) authenticationToken.getCredentials();
        String password = String.valueOf(passwords);
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("account",name);
        TUser tSysUser = userService.getOne(wrapper);
        if (tSysUser == null) {
            //这里返回后会报出对应异常
            return null;
        } else {
            if (LoginStatus.DISABLE.getCode().equals(tSysUser.getStatus())) {
                throw new BuzEx(StatusEnum.LOGIN_LOCK);
            }
            String pwd = new String(Base64Utils.decode(password.getBytes()));
            if (!BCrypt.checkpw(pwd, tSysUser.getPassword())) {
                throw new AuthenticationException();
            }
            tSysUser.setPassword(null);
            tSysUser.setSalt(null);

            //这里验证authenticationToken和simpleAuthenticationInfo的信息
            String token = JwtUtil.genToken(refreshUser(tSysUser), 60);
            String key = RedisConstants.UserCacheKey.USER_KEY.concat(tSysUser.getAccount());
            Object tokenValue = redisTemplate.opsForValue().get(key);
            if(tokenValue != null && !"".equals(tokenValue)){
                redisTemplate.delete(CommomConstants.keyPrefix.concat(tokenValue.toString()));
            }
            ThreadContext threadContext = ThreadContext.getInstance();
            threadContext.set(token);
            redisTemplate.opsForValue().set(key,token,2, TimeUnit.HOURS);
            simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, password, getName());
            return simpleAuthenticationInfo;
        }
    }


    public UserBaseDto refreshUser(TUser tSysUser){
        UserBaseDto userBaseDto = new UserBaseDto();
        userBaseDto.setUser(tSysUser);
        TRole tRole = roleService.selectRoleByUserId(tSysUser.getId());
        if(tRole != null){
            userBaseDto.setRole(tRole);
        }
        return userBaseDto;
    }
    /**
     * 左侧菜单通过递归算法实现树
     * @param parentId 父Id
     * @param menuList 当前所有菜单
     * @return
     */
    public List<MenuDto> recursionForMenu(int parentId,List<MenuDto> menuList){
        List<MenuDto> list = new ArrayList<>();
        /**
         * Optional.ofNullable(menuList).orElse(new ArrayList<>())  如果menuList是空的则返回一个new ArrayList<>()
         *  .stream() 返回List中的流
         *  .filter(menu -> menu.getParentId().equals(parentId)) 筛选List，返回只有条件成立的元素（当前元素的parentId必须等于父id）
         *  .forEach 遍历这个list
         */
        Optional.ofNullable(menuList).orElse(new ArrayList<>())
                .stream()
                .filter(menu -> menu.getParentKey().equals(parentId))
                .forEach(menu -> {
                    MenuDto MenuDto = new MenuDto();
                    MenuDto.setName(menu.getName());
                    //是否是目录
                 //   MenuDto.setAlwaysShow(menu.getLevel().equals(1)?true:false);
                    MenuDto.setPath(menu.getPath());
                    MenuDto.setId(menu.getId());
                    MenuDto.setComponent(StringUtils.hasLength(menu.getComponent())?menu.getComponent():"Layout");
                   // MenuDto.setMeta(new MetaVO(menu.getName(),menu.getIcon(),new ArrayList<>(Arrays.asList(1))));
                    List<MenuDto> children=recursionForMenu(menu.getId(),menuList);
                    MenuDto.setChildren(children);
                    list.add(MenuDto);
                });
        return list;
    }
}
