package com.qmth.themis.business.util;

import com.qmth.themis.business.constant.SpringContextHolder;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.AuthDto;
import com.qmth.themis.business.dto.cache.TEStudentCacheDto;
import com.qmth.themis.business.entity.TBSession;
import com.qmth.themis.business.entity.TBUser;
import com.qmth.themis.business.service.AuthInfoService;
import com.qmth.themis.business.service.ThemisCacheService;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.enums.Platform;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.signature.SignatureInfo;
import com.qmth.themis.common.signature.SignatureType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @Description: 鉴权工具类util
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/11/12
 */
public class AuthUtil {
    private final static Logger log = LoggerFactory.getLogger(AuthUtil.class);

    /**
     * admin鉴权
     *
     * @param platform
     * @param deviceId
     * @param authorization
     * @param time
     * @param sysUrls
     * @param request
     * @param response
     * @return
     */
    public static boolean adminAuthInterceptor(Platform platform,
                                               String deviceId,
                                               String authorization,
                                               long time,
                                               List<String> sysUrls,
                                               HttpServletRequest request,
                                               HttpServletResponse response) {
        ThemisCacheService themisCacheService = SpringContextHolder.getBean(ThemisCacheService.class);
        String url = request.getServletPath();
        String method = request.getMethod();
        TBSession tbSession = authHeadCommon(platform, deviceId, authorization, time, method, url);
        Long userId = Long.parseLong(tbSession.getIdentity());
        TBUser tbUser = themisCacheService.addAccountCache(userId);
        request.setAttribute(SystemConstant.SESSION, tbSession);
        request.setAttribute(SystemConstant.ACCOUNT, tbUser);
        return authFootCommon(userId, SystemConstant.userOauth, sysUrls, request, response);
    }

    /**
     * exam鉴权
     *
     * @param platform
     * @param deviceId
     * @param authorization
     * @param time
     * @param sysUrls
     * @param request
     * @param response
     * @return
     */
    public static boolean examAuthInterceptor(Platform platform,
                                              String deviceId,
                                              String authorization,
                                              long time,
                                              List<String> sysUrls,
                                              HttpServletRequest request,
                                              HttpServletResponse response) {
        ThemisCacheService themisCacheService = SpringContextHolder.getBean(ThemisCacheService.class);
        String url = request.getServletPath();
        String method = request.getMethod();
        TBSession tbSession = authHeadCommon(platform, deviceId, authorization, time, method, url);
        Long studentId = Long.parseLong(tbSession.getIdentity());
        TEStudentCacheDto teStudentCacheDto = themisCacheService.addStudentAccountCache(studentId);
        request.setAttribute(SystemConstant.SESSION, tbSession);
        request.setAttribute(SystemConstant.STUDENT_ACCOUNT, teStudentCacheDto);
        return authFootCommon(studentId, SystemConstant.studentOauth, sysUrls, request, response);
    }

    /**
     * websocket鉴权
     *
     * @param platform
     * @param deviceId
     * @param authorization
     * @param time
     * @param method
     * @param url
     * @return
     */
    public static TBSession websocketAuthInterceptor(Platform platform,
                                                     String deviceId,
                                                     String authorization,
                                                     long time,
                                                     String method,
                                                     String url) {
        return authHeadCommon(platform, deviceId, authorization, time, method, url);
    }

    /**
     * 鉴权头公用
     *
     * @param platform
     * @param deviceId
     * @param authorization
     * @param time
     * @return
     */
    static TBSession authHeadCommon(Platform platform,
                                    String deviceId,
                                    String authorization,
                                    long time,
                                    String method,
                                    String url) {
        RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
        if (SystemConstant.expire(time)) {
            log.warn("Authorization faile: time expired, server time=" + System.currentTimeMillis());
            throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
        }
        //测试
//        final SignatureInfo info = SignatureInfo.parse(authorization);
        //校验签名信息
        final SignatureInfo info = SignatureInfo.parse(method.toLowerCase(), url, time, authorization);
        if (info == null) {
            log.warn("Authorization faile: signature decode error");
            throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
        }
        if (SignatureType.TOKEN != info.getType()) {
            log.warn("Authorization faile: signature type is not Token");
            throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
        }
        //校验session
        String sessionId = info.getInvoker();
        TBSession tbSession = (TBSession) redisUtil.getUserSession(sessionId);
        if (Objects.isNull(tbSession)) {
            log.warn("Authorization faile: session id not exists: " + sessionId);
            throw new BusinessException(ExceptionResultEnum.NO_SESSION);
        }
        if (tbSession.getExpireTime() <= System.currentTimeMillis() || info.getTimestamp() > tbSession.getExpireTime()) {
            log.warn("Authorization faile: session has expired, expire time=" + tbSession.getExpireTime());
            throw new BusinessException(ExceptionResultEnum.TOKEN_INVALID);
        }
        if (!info.validate(tbSession.getAccessToken())) {
            log.warn("Authorization faile: access token invalid, session token is " + tbSession.getAccessToken());
            throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
        }
        if (!tbSession.getPlatform().equalsIgnoreCase(platform.name())) {
            log.warn("Authorization faile: platform invalid, session platform is " + tbSession.getPlatform());
            throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
        }
        if (!tbSession.getDeviceId().equalsIgnoreCase(deviceId)) {
            log.warn("Authorization faile: deviceId invalid, session deviceId is " + tbSession.getDeviceId());
            throw new BusinessException(ExceptionResultEnum.AUTHORIZATION_ERROR);
        }
        return tbSession;
    }

    /**
     * 鉴权尾公用
     *
     * @param userId
     * @param type
     * @param sysUrls
     * @param request
     * @param response
     * @return
     */
    static boolean authFootCommon(long userId,
                                  String type,
                                  List<String> sysUrls,
                                  HttpServletRequest request,
                                  HttpServletResponse response) {
        String url = request.getServletPath();
        ThemisCacheService themisCacheService = SpringContextHolder.getBean(ThemisCacheService.class);
        //验证权限
        AuthDto authDto = null;
        if (type.contains(SystemConstant.userOauth)) {
            authDto = themisCacheService.addAccountAuthCache(userId);
        } else if (type.contains(SystemConstant.studentOauth)) {
            authDto = themisCacheService.addStudentAuthCache(userId);
        }
        request.setAttribute(SystemConstant.ORG, authDto.getTbOrg());

        //系统管理员拥有所有权限
//        if (authDto.getRoleCodes().contains(RoleEnum.SUPER_ADMIN.name())) {
//            return true;
//        }
        AuthInfoService authInfoService = SpringContextHolder.getBean(AuthInfoService.class);
        if (Objects.nonNull(authDto.getTbOrg())) {
            authInfoService.appHasExpired(authDto.getTbOrg().getCode());
        }
        //系统公用接口不拦截
        int sysCount = (int) sysUrls.stream().filter(s -> s.equalsIgnoreCase(url)).count();
        if (sysCount > 0) {
            return true;
        }
        Set<String> urls = authDto.getUrls();
        int count = (int) urls.stream().filter(s -> s.equalsIgnoreCase(url)).count();
        if (count == 0) {
            log.warn("Authorization faile: url cannot access");
            throw new BusinessException(ExceptionResultEnum.UN_AUTHORIZATION);
        }
        response.setStatus(ExceptionResultEnum.SUCCESS.getCode());
        return true;
    }
}
