package com.sunyard.chsm.auth;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.sunyard.chsm.mapper.CryptoServiceApiMapper;
import com.sunyard.chsm.model.R;
import com.sunyard.chsm.model.entity.CryptoServiceApi;
import com.sunyard.chsm.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.sunyard.chsm.constant.SecurityConstant.ATTRIBUTE_APP_USER;

/**
 * @author liulu
 * @since 2024/11/13
 */
@Slf4j
@Component
public class AuthHandler implements HandlerInterceptor, InitializingBean {

    Cache<String, Map<String, List<Long>>> cache = null;

    /**
     * token 过期时间, 分钟
     */
    @Value("${chsm.token.expireTime:720}")
    private Integer tokenExpireTime;

    @Resource
    private CryptoServiceApiMapper cryptoServiceApiMapper;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        AppUser user = (AppUser) request.getAttribute(ATTRIBUTE_APP_USER);
        if (Objects.isNull(user)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        AuthCode authCode = handlerMethod.getMethodAnnotation(AuthCode.class);
        if (authCode == null || ObjectUtils.isEmpty(authCode.value())) {
            return true;
        }

        String code = authCode.value();
        Map<String, List<Long>> codeServiceMap = cache.get(user.getTokenId(), k -> {
            List<CryptoServiceApi> apis = cryptoServiceApiMapper.selectByServiceIds(user.getServiceIds());
            return apis.stream().collect(Collectors.groupingBy(CryptoServiceApi::getApiCode,
                    Collectors.mapping(CryptoServiceApi::getCryptoServiceId, Collectors.toList())));
        });
        if (codeServiceMap == null || !codeServiceMap.containsKey(code)) {
            log.warn("app: {}-{}, 无权访问: {} - {}", user.getAppId(), user.getName(), code, request.getRequestURI());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getOutputStream().write(JsonUtils.toJsonBytes(R.error("无权访问")));
            return false;
        }
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        cache = Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofMinutes(tokenExpireTime))
                .build();
    }
}
