package com.multictrl.modules.security.oauth2;

import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.ConvertUtils;
import com.multictrl.common.utils.JsonUtils;
import com.multictrl.common.utils.MessageUtils;
import com.multictrl.modules.security.entity.SysUserTokenEntity;
import com.multictrl.modules.security.service.ShiroService;
import com.multictrl.modules.security.user.UserDetail;
import com.multictrl.modules.sys.entity.SysUserEntity;
import lombok.AllArgsConstructor;
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.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 认证
 *
 * @author Sdy
 */
@Component
@AllArgsConstructor
public class Oauth2Realm extends AuthorizingRealm {
    private final ShiroService shiroService;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof Oauth2Token;
    }

    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        UserDetail user = (UserDetail) principals.getPrimaryPrincipal();

        Set<String> permsSet;
        Object object = CacheUtils.get(user.getToken() + "_perms");
        if (object == null) {
            //用户权限列表
            permsSet = shiroService.getUserPermissions(user);
            Map<String, Object> map = new HashMap<>();
            map.put("perms", JsonUtils.toJsonString(permsSet));
            CacheUtils.set(user.getToken() + "_perms", map);
        } else {
            String perms = (String) ((Map<String, Object>) object).get("perms");
            permsSet = JsonUtils.parseObject(perms, Set.class);
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permsSet);
        return info;
    }

    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String accessToken = (String) token.getPrincipal();

        UserDetail userDetail;
        Object object = CacheUtils.get(accessToken);
        if (object == null) {
            //根据accessToken，查询用户信息
            SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken);

            //token失效
            if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
                throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID));
            }

            //查询用户信息
            SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId());
            //转换成UserDetail对象
            userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);
            userDetail.setToken(accessToken);
            //账号锁定
            if (userDetail.getStatus() == 0) {
                throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK));
            }

            //获取用户对应的部门数据权限
            List<Long> deptIdList = shiroService.getDataScopeList(userDetail.getId());
            userDetail.setDeptIdList(deptIdList);

            Map<String, Object> map = new HashMap<>();
            map.put("user", userDetail);
            map.put("token", tokenEntity);
            CacheUtils.set(accessToken, map, 1000 * 60);
        } else {
            userDetail = (UserDetail) ((Map<String, Object>) object).get("user");
            SysUserTokenEntity tokenEntity = (SysUserTokenEntity) ((Map<String, Object>) object).get("token");

            //token失效
            if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
                throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID));
            }
        }

        return new SimpleAuthenticationInfo(userDetail, accessToken, getName());
    }

}