package com.fdage.shiro;

import com.fdage.pojo.TbResource;
import com.fdage.pojo.TbRole;
import com.fdage.pojo.TbUser;
import com.fdage.service.IResourceService;
import com.fdage.service.IRoleService;
import com.fdage.service.IUserService;
import lombok.extern.log4j.Log4j2;
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.Service;

import java.math.BigInteger;
import java.util.*;

/**
 * 配置不校验的话，是不会走这个方法的
 */
@Log4j2
@Service
public class MyRealm extends AuthorizingRealm {



    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private IUserService userService;

    @Autowired
    private IRoleService roleService;

    @Autowired
    private IResourceService resourceService;



    /**
     * 大坑！，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     *
     * principals： 是token
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = JwtUtil2.getUsername(principals.toString());

        TbUser dbUserEntity = userService.findByUserName(username);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        Map<String, Set<String>> resultMap = getRole(dbUserEntity);
        Set<String> rolekeys = resultMap.get("role");


        // 获取用户权限
        Set<String> permissions = new HashSet<>();
        try {
            List<TbResource> userPermissionList = resourceService.getResourcesByUserPermission(dbUserEntity);
            for (TbResource i : userPermissionList) {
                permissions.add(i.getResourceKey());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        // 设置角色、权限
        simpleAuthorizationInfo.addRoles(rolekeys);
        simpleAuthorizationInfo.addStringPermissions(permissions);
        return simpleAuthorizationInfo;
    }

    /**
     * 获取角色相关信息
     */
    private Map<String, Set<String>> getRole(TbUser userEntity){

        Set<Long> roleIds = userService.findUserRoleByUserId(userEntity.getId());
        Set<String> roleKeys = new HashSet<>();
        for (Long i : roleIds) {
            TbRole dbRoleEntity = roleService.findById(i.longValue());
            roleKeys.add(dbRoleEntity.getRoleKey());
        }

        HashMap<String, Set<String>> resultMap = new HashMap<>();
        resultMap.put("role", roleKeys);

        return resultMap;
    }

    /**
     * 默认使用此方法进行用户名正确与否验证，错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        // 解密获得username，用于和数据库进行对比
        if(token == null){
            log.error("error token");
            throw new JwtAuthenticationException(5001, "token is null");
        }
        String username = JwtUtil2.getUsername(token);
        if (username == null) {
            log.error("error token username");
            throw new JwtAuthenticationException(5001, "token invalid");
        }

        TbUser userEntity = userService.findByUserName(username);
        if (userEntity == null) {
            log.error("error token userEntity");
            throw new JwtAuthenticationException(5001, "User didn't existed!");
        }

        // 校验请求token是否跟redis token一致
        String redisToken = redisTemplate.opsForValue().get(userEntity.getUserName());
        if (!token.equals(redisToken)) {
            log.error("error token redis");
            throw new JwtAuthenticationException(5001, "token invalid");

        }


        if (! JwtUtil2.isVerify(token, username)) {
            log.error("error token username or password");
            throw new JwtAuthenticationException(5001, "token invalid");
        }


        return new SimpleAuthenticationInfo(token, token, "my_realm");
    }
}
