package com.keyware.shandan.frame.config.security;

import com.keyware.shandan.common.util.StringUtils;
import com.keyware.shandan.system.entity.SysUser;
import com.keyware.shandan.system.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.rememberme.InvalidCookieException;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Component;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

/**
 * Spring Security工具类
 */
@Slf4j
@Component
public class SecurityUtil {

    @Autowired
    private SysUserService userService;

    @Autowired
    private SessionRegistry sessionRegistry;

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;


    /**
     * 获取登录的用户信息
     *
     * @return
     */
    public SysUser getLoginUser() {
        User user = getLoginUserByContext();
        SysUser sysUser = null;
        if (user != null) {
            sysUser = userService.findByLoginName(user.getUsername());
        }
        return sysUser;
    }

    /**
     * 从ThreadLocal获取其自己的SecurityContext，从而获取在Security上下文中缓存的登录用户
     */
    public static User getLoginUserByContext() {
        User user = null;
        SecurityContext ctx = SecurityContextHolder.getContext();
        Authentication auth = ctx.getAuthentication();
        if (auth.getPrincipal() != null) {
            if (auth.getPrincipal() instanceof User) {
                user = (User) auth.getPrincipal();
            } else if (auth.getPrincipal() instanceof String) {
                String username = (String) auth.getPrincipal();
                user = new User(username, "", new ArrayList<>());
            }
        }
        return user;
    }

    public static String getLoginUsername() {
        SecurityContext ctx = SecurityContextHolder.getContext();
        Authentication auth = ctx.getAuthentication();
        if (auth.getPrincipal() instanceof String) {
            return (String) auth.getPrincipal();
        }
        return null;
    }

    /**
     * 获取登录用户信息
     *
     * @return
     */
    public static SysUser getLoginSysUser() {
        User user = getLoginUserByContext();
        SysUser sysUser = null;
        if (user != null) {
            sysUser = UserConfig.getLoginCacheUserByLoginName(user.getUsername());
        }
        return sysUser;
    }

    /**
     * 清除remember-me持久化tokens
     */
    public void rememberMeRemoveUserTokens(String userName) {
        persistentTokenRepository.removeUserTokens(userName);
    }

    /**
     * 根据rememberMeCookie查询获取数据表中的信息
     */
    public PersistentRememberMeToken rememberMeGetTokenForSeries(Cookie rememberMeCookie) {
        return rememberMeCookie == null ? null : persistentTokenRepository.getTokenForSeries(SecurityUtil.decodeCookie(rememberMeCookie.getValue())[0]);
    }

    /**
     * 解密rememberMeCookie
     * 详情可在 PersistentTokenBasedRememberMeServices.processAutoLoginCookie断点，查看调用栈
     */
    public static String[] decodeCookie(String cookieValue) throws InvalidCookieException {
        StringBuilder cookieBuilder = new StringBuilder(cookieValue);
        for (int j = 0; j < cookieValue.length() % 4; ++j) {
            cookieBuilder.append("=");
        }

        try {
            Base64.getDecoder().decode(cookieValue.getBytes());
        } catch (IllegalArgumentException var7) {
            throw new InvalidCookieException("Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
        }

        String cookieAsPlainText = new String(Base64.getDecoder().decode(cookieValue.getBytes()));
        String[] tokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");

        for (int i = 0; i < tokens.length; ++i) {
            try {
                tokens[i] = URLDecoder.decode(tokens[i], StandardCharsets.UTF_8.toString());
            } catch (UnsupportedEncodingException var6) {
                log.error(var6.getMessage(), var6);
            }
        }

        return tokens;
    }

    /**
     * 获取 "remember-me" cookie
     */
    public static Cookie getRememberMeCookie(HttpServletRequest request) {
        if(request.getCookies() != null){
            for (Cookie cookie : request.getCookies()) {
                if ("remember-me".equals(cookie.getName())) {
                    return cookie;
                }
            }
        }
        return null;
    }





    /*  SessionRegistry相关操作  */


    /**
     * 根据user从sessionRegistry获取SessionInformation
     */
    public List<SessionInformation> sessionRegistryGetSessionInformationList(User user) {
        return sessionRegistry.getAllSessions(user, true);
    }

    /**
     * 根据sessionId从sessionRegistry获取用户
     */
    public User sessionRegistryGetUserBySessionId(String sessionId) {
        SessionInformation sessionInformation = sessionRegistry.getSessionInformation(sessionId);
        if (sessionInformation != null && !sessionInformation.isExpired()) {
            Object principal = sessionInformation.getPrincipal();
            if (principal instanceof String) {
                return new User((String) principal, "", new ArrayList<>());
            } else if (principal instanceof User) {
                return (User) principal;
            }
        }
        return null;
    }

    /**
     * 从sessionRegistry中删除user
     */
    public void sessionRegistryRemoveUser(User user) {
        List<SessionInformation> allSessions = this.sessionRegistryGetSessionInformationList(user);
        if (allSessions != null) {
            for (SessionInformation sessionInformation : allSessions) {
                sessionInformation.expireNow();
                sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
            }

            //清除当前的上下文
            SecurityContextHolder.clearContext();

            //清除remember-me持久化tokens
            this.rememberMeRemoveUserTokens(user.getUsername());

            UserConfig.removeLoginUser(user.getUsername());
        }
    }

    /**
     * 指定loginName从sessionRegistry中删除user
     */
    public void sessionRegistryRemoveUserByLoginName(String loginName) {
        //清除remember-me持久化tokens
        this.rememberMeRemoveUserTokens(loginName);
        UserConfig.removeLoginUser(loginName);
        List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
        for (Object principal : allPrincipals) {
            String username = "";
            if (principal instanceof String) {
                username = (String) principal;
            } else if (principal instanceof User) {
                username = ((User) principal).getUsername();
            }

            if (username.equals(loginName)) {
                List<SessionInformation> allSessions = sessionRegistry.getAllSessions(principal, false);
                for (SessionInformation sessionInformation : allSessions) {
                    sessionInformation.expireNow();
                    sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
                    SecurityContextHolder.clearContext();
                }
                break;
            }
        }
    }

    /**
     * 向sessionRegistry注册user
     */
    public void sessionRegistryAddUser(String sessionId, Object user) {
        sessionRegistry.registerNewSession(sessionId, user);
    }

    public List<Object> sessionRegistryGetAllPrincipals() {
        return sessionRegistry.getAllPrincipals();
    }
}