package com.qmth.distributed.print.auth;

import com.qmth.boot.core.enums.Platform;
import com.qmth.boot.core.security.model.AccessEntity;
import com.qmth.boot.core.security.service.AuthorizationService;
import com.qmth.boot.tools.signature.SignatureType;
import com.qmth.distributed.print.business.service.AuthInfoService;
import com.qmth.teachcloud.common.bean.auth.AuthBean;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.entity.TBSession;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.PrivilegePropertyEnum;
import com.qmth.teachcloud.common.enums.RoleTypeEnum;
import com.qmth.teachcloud.common.service.CommonCacheService;
import com.qmth.teachcloud.common.util.RedisUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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

@Component
public class DistributedPrintAuthenticationService implements AuthorizationService {
    private final static Logger log = LoggerFactory.getLogger(DistributedPrintAuthenticationService.class);

    @Resource
    CommonCacheService commonCacheService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    DictionaryConfig dictionaryConfig;

    @Resource
    AuthInfoService authInfoService;

    @Override
    public AccessEntity findByIdentity(String identity, SignatureType signatureType, String path) {
        return new DistributedPrintSession(identity, SignatureType.TOKEN);
    }

    @Override
    public boolean hasPermission(AccessEntity accessEntity, String path) {
        if (Objects.nonNull(accessEntity) && Objects.nonNull(accessEntity.getIdentity())) {
            TBSession tbSession = (TBSession) redisUtil.getUserSession(accessEntity.getIdentity());
            if (Objects.isNull(tbSession)) {
                log.warn("Authorization faile: session id not exists: {}", accessEntity.getIdentity());
                throw ExceptionResultEnum.NOT_LOGIN.exception();
            }
            if (tbSession.getExpireTime() <= System.currentTimeMillis()) {
                log.warn("Authorization faile: session has expired, expire time={}", tbSession.getExpireTime());
                throw ExceptionResultEnum.NOT_LOGIN.exception();
            }
            Platform platform = ServletUtil.getRequestPlatform();
            String deviceId = ServletUtil.getRequestDeviceId();
            if (!tbSession.getPlatform().equalsIgnoreCase(platform.name())) {
                log.warn("Authorization faile: platform invalid, session platform is {}", tbSession.getPlatform());
                throw ExceptionResultEnum.AUTHORIZATION_ERROR.exception();
            }
            if (!tbSession.getDeviceId().equalsIgnoreCase(deviceId)) {
                log.warn("Authorization faile: deviceId invalid, session deviceId is {} ", tbSession.getDeviceId());
                throw ExceptionResultEnum.AUTHORIZATION_ERROR.exception();
            }
            Long userId = Long.parseLong(tbSession.getIdentity());
            SysUser sysUser = commonCacheService.userCache(userId);
            HttpServletRequest request = ServletUtil.getRequest();
            HttpServletResponse response = ServletUtil.getResponse();
            request.setAttribute(SystemConstant.SESSION, tbSession);
            request.setAttribute(SystemConstant.USER, sysUser);
            boolean auth = authFootCommon(userId, SystemConstant.USER_OAUTH_CACHE, path, request, response);
            if (auth) {
                Long expireTime = redisUtil.getUserSessionExpire(accessEntity.getIdentity());
                if (Objects.nonNull(expireTime) && expireTime.longValue() > -1L) {
                    if (Objects.nonNull(tbSession.getLastAccessTime()) && (System.currentTimeMillis() - tbSession.getLastAccessTime()) / 1000 > dictionaryConfig.sysDomain().getSessionActive().getSeconds()) {
                        log.warn("Authorization faile: session active, session active is {}", dictionaryConfig.sysDomain().getSessionActive().getSeconds());
                        throw ExceptionResultEnum.NOT_LOGIN.exception();
                    }
                    tbSession.setLastInfo();
                    redisUtil.setUserSession(accessEntity.getIdentity(), tbSession, expireTime);
                }
            }
            return auth;
        }
        return false;
    }

    /**
     * 鉴权尾公用
     *
     * @param userId
     * @param type
     * @param path
     * @param request
     * @param response
     * @return
     */
    public boolean authFootCommon(Long userId,
                                  String type,
                                  String path,
                                  HttpServletRequest request,
                                  HttpServletResponse response) {
        //验证权限
        AuthBean authBean = type.contains(SystemConstant.USER_OAUTH_CACHE) ? authBean = commonCacheService.userAuthCache(userId) : null;
        if (Objects.isNull(authBean)) {
            throw ExceptionResultEnum.ROLE_ENABLE_AUTHORIZATION.exception();
        }
        request.setAttribute(SystemConstant.SCHOOL, authBean.getSchool());
        request.setAttribute(SystemConstant.ORG, authBean.getOrg());

        //超级系统管理员拥有所有权限
        int count = Objects.nonNull(authBean) ? (int) authBean.getRoleList().stream().filter(s -> Objects.equals(s.getName(), RoleTypeEnum.ADMIN.getDesc())).count() : 0;
        if (count > 0) {
            return true;
        }
        if (Objects.nonNull(authBean.getSchool())) {
            authInfoService.appHasExpired(authBean.getSchool().getCode());
        }
        //系统公用接口不拦截
        Set<String> sysUrls = commonCacheService.privilegeUrlCache(PrivilegePropertyEnum.SYS, SystemConstant.getHeadOrUserSchoolId());
        int sysCount = Objects.nonNull(sysUrls) ? (int) sysUrls.stream().filter(s -> s.equalsIgnoreCase(path)).count() : 0;
        if (sysCount > 0) {
            return true;
        }
        Set<String> urls = authBean.getUrls();
        int privilegeCount = Objects.nonNull(urls) ? (int) urls.stream().filter(s -> s.equalsIgnoreCase(path)).count() : 0;
        if (privilegeCount == 0) {
            log.warn("Authorization faile: url cannot access");
            throw ExceptionResultEnum.UN_AUTHORIZATION.exception();
        }
        response.setStatus(ExceptionResultEnum.SUCCESS.getCode());
        return true;
    }
}
