package cn.edu.whu.boot.user.realms;

import cn.edu.whu.boot.user.entities.Role;
import cn.edu.whu.boot.user.entities.User;
import cn.edu.whu.boot.user.services.*;
import org.apache.shiro.authc.*;
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.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Optional;
import java.util.Set;

@Component
public class UserRealm extends AuthorizingRealm {

    private final Logger log = LoggerFactory.getLogger(UserRealm.class);

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private SessionService sessionService;

    @Autowired
    private PermissionCacheService cacheService;

    /**
     * 授权，确定用户具有哪些权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        if (principalCollection == null) {
            return null;
        }
        User user = (User) getAvailablePrincipal(principalCollection);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        Integer role = user.getRole();
        if (role == null) {
            return simpleAuthorizationInfo;
        }
        // 查询用户权限
        if (cacheService.permissionsCached(role)) {
            // 先查询缓存
            simpleAuthorizationInfo.setStringPermissions(cacheService.getPermissionsByRole(role));
        } else {
            // 如果没有缓存，则查询数据库并更新缓存
            // 超级管理员（1）拥有所有权限
            // 决策人员（2）拥有管理员（3）的全部权限
            Set<String> dbPermissions = role == 1 ? permissionService.findAllDescriptions() : permissionService.findByRole(role == 2 ? 3 : role);
            simpleAuthorizationInfo.setStringPermissions(dbPermissions);
            cacheService.addPermissions(role, dbPermissions.toArray(new String[0]));
        }
        return simpleAuthorizationInfo;
    }

    /**
     * 认证，即检查用户名和密码是否正确
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
        String username = upToken.getUsername();
        // 检查用户名是否为空
        if (username == null) {
            throw new AccountException("用户名不能为空");
        }
        // 并发登录控制
        // 查找和当前尝试登录的用户具有相同用户名的 session，
        // 如果存在这样的 session，就删除旧的 session，实现强制下线
        sessionService
                .getAllSessions()
                .stream()
                .filter(session ->
                                Optional.ofNullable((SimplePrincipalCollection) session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY))
                                        .map(simplePrincipalCollection -> {
                                            User onlineUser = (User) simplePrincipalCollection.getPrimaryPrincipal();
                                            return onlineUser.getUsername().equals(username);
                                        })
                                        .orElse(false)
                       )
                .findFirst()
                .ifPresent(session -> {
                    log.info(String.format("Session %s expired because of the limit of concurrent logging in.", (String) session.getId()));
                    Subject subject = new Subject.Builder().sessionId(session.getId()).buildSubject();
                    subject.logout();
                }); // 删除 session
        // 查询用户
        Optional<User> optionalUser = userService.findByUsername(username);
        if (!optionalUser.isPresent()) {
            throw new UnknownAccountException(String.format("用户 %s 不存在", username));
        }
        User user = optionalUser.get();
        Optional<Role> optionalRole = roleService.findById(user.getRole());
        if (!optionalRole.isPresent()) {
            throw new AuthenticationException("角色不存在");
        }
        return new SimpleAuthenticationInfo(
                user,
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),
                user.getUsername()
        );
    }
}