/* (C) 2022 YiRing, Inc. */
package com.yiring.auth.util;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import com.yiring.auth.domain.user.User;
import com.yiring.auth.domain.user.UserRepository;
import com.yiring.common.core.Status;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * 认证工具类
 *
 * @author Jim
 * @version 0.1
 * 2022/4/8 17:34
 */

@SuppressWarnings("unused")
@Component
@RequiredArgsConstructor
public class Auths {

    final UserRepository userRepository;

    /**
     * 管理员用户
     */
    public static final String ADMIN_USER = "admin";

    /**
     * 管理员角色标识
     */
    public static final List<String> ADMIN_ROLES = List.of("admin", "super-admin", "platform-admin", "data-admin");

    /**
     * 根据 Token 获取用户信息
     * 如果用户未登录或校验失败会抛出 NotLoginException {@link Status#UNAUTHORIZED}
     *
     * @param token token
     * @return 用户信息
     */
    public User getUserByToken(@NonNull String token) {
        Object id = StpUtil.getLoginIdByToken(token);
        if (id == null) {
            StpUtil.logoutByTokenValue(token);
            throw NotLoginException.newInstance(StpUtil.TYPE, null, "Token 无效", token);
        }

        Optional<User> optional = userRepository.findById(Objects.toString(id));
        if (
            optional.isEmpty() ||
            Boolean.TRUE.equals(optional.get().getDeleted()) ||
            Boolean.TRUE.equals(optional.get().getDisabled())
        ) {
            StpUtil.logout(id);
            throw NotLoginException.newInstance(StpUtil.TYPE, NotLoginException.INVALID_TOKEN, "用户被禁用", token);
        }

        return optional.get();
    }

    /**
     * 获取当前登录用户
     * 如果用户未登录会抛出 NotLoginException {@link Status#UNAUTHORIZED}
     */
    public User getLoginUser() {
        String token = StpUtil.getTokenValue();
        if (token == null) {
            throw NotLoginException.newInstance(StpUtil.TYPE, null, "用户未登录", null);
        }

        return getUserByToken(token);
    }

    /**
     * 踢出这个用户 id 所有登录状态（可能有多人重复登录了一个账号的情况）
     *
     * @param userId 用户 id
     */
    public void logoutAll(String userId) {
        List<String> tokens = StpUtil.getTokenValueListByLoginId(userId);
        for (String token : tokens) {
            StpUtil.logoutByTokenValue(token);
        }
    }

    /**
     * 判断用户是否为超级管理员
     *
     * @param userId 用户 ID
     * @return 是否为管理员
     */
    public boolean isAdmin(String userId) {
        Optional<User> optional = userRepository.findById(userId);
        return optional.filter(this::isAdmin).isPresent();
    }

    /**
     * 检查用户是否为管理员（检查用户是否拥有包含 admin 字符的角色）
     *
     * @param user 用户
     * @return 是否为管理员
     */
    public boolean isAdmin(User user) {
        return (
            ADMIN_USER.equals(user.getUsername()) ||
            user
                .getRoles()
                .stream()
                .anyMatch(role -> Boolean.FALSE.equals(role.getDisabled()) && ADMIN_ROLES.contains(role.getUid()))
        );
    }

    /**
     * 检查用户是否拥有指定角色
     *
     * @param user  用户
     * @param roles 角色列表
     * @return 是否拥有指定角色
     */
    public boolean matchRole(User user, List<String> roles) {
        return user
            .getRoles()
            .stream()
            .anyMatch(role ->
                !Boolean.TRUE.equals(role.getDisabled()) &&
                !Boolean.TRUE.equals(role.getDeleted()) &&
                roles.contains(role.getUid())
            );
    }

    /**
     * 检查当前登录用户是否为管理员
     * {@link this.isAdmin}
     *
     * @return 是否为管理员
     */
    public boolean checkLoginUserIsAdmin() {
        return isAdmin(getLoginUser());
    }
}
