package com.njcn.shiro.filter;


import com.njcn.enums.CookieKeyEnum;
import com.njcn.enums.LogTypeEnum;
import com.njcn.enums.ProjectEnum;
import com.njcn.service.log.UserLogDetailService;
import com.njcn.shiro.cache.VCache;
import com.njcn.shiro.session.UserSessionManager;
import com.njcn.shiro.session.UserShiroSessionRepository;
import com.njcn.shiro.token.TokenManager;
import com.njcn.sso.shiro.session.SessionStatus;
import com.njcn.utils.AppConfig;
import com.njcn.utils.PubUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 *@author hongawen
 *@date 2018/3/21 19:48
 */
public class KickoutSessionFilter extends AccessControlFilter {

    private static final Logger logger = LoggerFactory.getLogger(KickoutSessionFilter.class);

    //踢出状态，true标示踢出
    public final static String KICKOUT_STATUS = "kickout_status";

    private static UserShiroSessionRepository userShiroSessionRepository;


    @Autowired
    private AppConfig appConfig;

    @Autowired
    private UserLogDetailService userLogDetailService;



    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        Subject subject = getSubject(request, response);
        Session session = subject.getSession();
        Serializable sessionId = session.getId();
        String userId = TokenManager.getUserId();
        Boolean marker = (Boolean) session.getAttribute(KICKOUT_STATUS);
        if (null != marker && marker) {
            return Boolean.FALSE;
        }
        //从缓存获取用户-Session信息 <UserId,SessionId>
        List<String> infoMap = VCache.get(userId, ArrayList.class);
        //如果不存在，创建一个新的
        infoMap = null == infoMap ? new ArrayList<>() : infoMap;
        String token = (String) TokenManager.getSession().getAttribute(CookieKeyEnum.SSOLOGIN.getCookieKey());
        String set = sessionId + "shiro" + token;
        List<String> delSeesionId = new ArrayList<>();
        if (infoMap.size() > 0) {
            for (String value : infoMap) {
                if (!value.endsWith(token)) {
                    delSeesionId.add(value);
                    String oldSessionId = value.substring(0, value.indexOf("shiro"));
                    Session oldSession = userShiroSessionRepository.getSession(oldSessionId);
                    if (null != oldSession) {
                        //标记session被挤下线
                        SessionStatus sessionStatus = new SessionStatus();
                        sessionStatus.setOnlineStatus(false);
                        oldSession.setAttribute(UserSessionManager.SESSION_STATUS, sessionStatus);
                        oldSession.setAttribute(KICKOUT_STATUS, Boolean.TRUE);
                        userShiroSessionRepository.saveSession(oldSession);
                        logger.error("该用户之前的会话已被挤下线，用户ID为" + userId);
                    }
                }
            }
        } else {
            //第一个，直接存进去便可
            infoMap.add(set);
        }
        //如果集合中不存在当前set，则增加
        if(!infoMap.contains(set)){
            infoMap.add(set);
        }
        //根据sessionID清理被挤下线的
        if (!CollectionUtils.isEmpty(delSeesionId)) {
            for (String id : delSeesionId) {
                infoMap.remove(id);
            }
        }
        //存储到缓存1个小时（这个时间最好和session的有效期一致或者大于session的有效期）
        VCache.setex(userId, infoMap, 3600);
        return Boolean.TRUE;
    }

    /**
     * 被挤下线之后，前往sso中心注销当前的ssoToken下的session
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request,
                                     ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = ((HttpServletResponse) response);
        HttpServletRequest httpRequest = (HttpServletRequest) request;
//        userLogDetailService.saveUserLog(TokenManager.getToken().getUserIndex(), TokenManager.getToken().getLoginName(),"被挤下线","失败",appConfig.getSystemFlag(), PubUtils.getIpAddr(httpRequest), LogTypeEnum.SYSTEM.toString(),2);
        //判断是不是Ajax请求
        if (ShiroFilterUtils.isAjax(httpRequest)) {
            httpResponse.sendError(301);
            ShiroFilterUtils.out(response, "该用户被挤下线");
        }else{
            boolean flag = appConfig.isOneContainer();
            String url;
            if (flag) {
                String baseUrl = httpRequest.getRequestURL().toString();
                if (baseUrl.contains(ProjectEnum.PQS9200.getItem())) {
                    url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.PQS9200.getItem())) + ProjectEnum.PQS9200.getLoginOut();
                } else if (baseUrl.contains(ProjectEnum.PQS9000.getItem())) {
                    url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.PQS9000.getItem())) + ProjectEnum.PQS9000.getLoginOut();
                } else if (baseUrl.contains(ProjectEnum.PQS9300.getItem())) {
                    url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.PQS9300.getItem())) + ProjectEnum.PQS9300.getLoginOut();
                } else if (baseUrl.contains(ProjectEnum.PQS9900.getItem())) {
                    url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.PQS9900.getItem())) + ProjectEnum.PQS9900.getLoginOut();
                }else {
                    url = baseUrl.substring(0, baseUrl.indexOf(ProjectEnum.SSO.getItem())) + ProjectEnum.SSO.getItem();
                }
            } else {
                url = appConfig.getSsoUrl() + "/loginout";
            }
            WebUtils.issueRedirect(httpRequest, response, url);
        }
        return false;
    }

    public UserShiroSessionRepository getUserShiroSessionRepository() {
        return userShiroSessionRepository;
    }

    public static void setUserShiroSessionRepository(UserShiroSessionRepository userShiroSessionRepository) {
        KickoutSessionFilter.userShiroSessionRepository = userShiroSessionRepository;
    }
}
