package com.bcxin.intercepters;

import cn.myapps.components.HmacComponent;
import com.bcxin.intercepters.values.SecretSaltValueType;
import com.bcxin.saas.core.components.JsonProvider;
import com.bcxin.saas.core.exceptions.SaasForbidException;
import com.bcxin.saas.core.exceptions.SaasUnAuthorizeException;
import com.bcxin.saas.domains.entities.HmacAuthEntity;
import com.bcxin.saas.domains.repositories.HmacAuthRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Date;

public class HmacAuthInterceptor implements HandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(HmacAuthInterceptor.class);
    private final HmacAuthRepository hmacAuthRepository;
    private final StringRedisTemplate redisTemplate;
    private final JsonProvider jsonProvider;
    private final HmacComponent hmacComponent;

    // 允许的时间差（毫秒）
    private static final long MAX_TIME_DIFF = 7 * 24 * 60 * 60 * 1000;

    public HmacAuthInterceptor(HmacAuthRepository hmacAuthRepository, StringRedisTemplate redisTemplate, JsonProvider jsonProvider, HmacComponent hmacComponent) {
        this.hmacAuthRepository = hmacAuthRepository;
        this.redisTemplate = redisTemplate;
        this.jsonProvider = jsonProvider;
        this.hmacComponent = hmacComponent;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String clientId = null;
        String signature = null;
        String timestampStr = null;
        try {
            clientId = request.getHeader("x-client-id");
            signature = request.getHeader("x-signature");
            timestampStr = request.getHeader("x-timestamp");

            if (clientId == null || signature == null || timestampStr == null) {
                response.sendError(HttpStatus.UNAUTHORIZED.value(), "x-client-Id;x-signature;x-timestamp 是必填的");
                return false;
            }

            long timestamp;
            try {
                timestamp = Long.parseLong(timestampStr);
            } catch (NumberFormatException e) {
                response.sendError(HttpStatus.UNAUTHORIZED.value(), "无效的x-timestamp格式");
                return false;
            }

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            long currentTime = Long.parseLong(dateFormat.format(new Date()));
            if (Math.abs(currentTime - timestamp) > MAX_TIME_DIFF) {
                response.sendError(HttpStatus.UNAUTHORIZED.value(), "请求已过期");
                return false;
            }

            SecretSaltValueType saltValue = getSecretSalt(clientId,request.getRequestURI());

            String data = String.format("%s#%s", timestampStr, saltValue.getDataToSign());
            String computedSignature = this.calculateHmac(saltValue.getSecret(), data);
            if (!signature.equalsIgnoreCase(computedSignature)) {
                response.sendError(HttpStatus.UNAUTHORIZED.value(), "x-signature签名内容无效");
                return false;
            }

            return HandlerInterceptor.super.preHandle(request, response, handler);
        } catch (Exception e) {
            logger.error("回调请求发生异常(clientId={},signature={},timestampStr={}):url={}", clientId, signature, timestampStr, request, e);

            throw e;
        }
    }

    private SecretSaltValueType getSecretSalt(String clientId,String url) {
        String cacheKey = String.format("com:bcx:hmac:%s", clientId);
        String secretValueString = this.redisTemplate.opsForValue().get(cacheKey);

        SecretSaltValueType saltValue = this.jsonProvider.getData(secretValueString, SecretSaltValueType.class);
        if (saltValue == null) {
            // 从数据库中获取密钥
            HmacAuthEntity hmacAuthEntity = hmacAuthRepository.findByClientId(clientId);
            if (hmacAuthEntity != null) {
                if (hmacAuthEntity.isExpired()) {
                    throw new SaasUnAuthorizeException("密钥已过期");
                }

                saltValue = SecretSaltValueType.create(
                        hmacAuthEntity.getSecret(),
                        hmacAuthEntity.getDataToSign(),
                        hmacAuthEntity.getResource()
                );

                secretValueString = this.jsonProvider.getJson(saltValue);
                this.redisTemplate.opsForValue().set(cacheKey, secretValueString, Duration.of(1, ChronoUnit.HOURS));
            } else {
                throw new SaasUnAuthorizeException("密钥无效");
            }
        }

        if (!saltValue.isAllowedResource(url)) {
            throw new SaasForbidException(String.format("该授权码不允许访问此资源:%s, 请检查后重试", url));
        }

        return saltValue;
    }



    /**
     * 计算 HMAC-SHA256 签名，返回Base64编码的字符串
     *
     * @param data 要签名的数据
     * @param key  密钥
     * @return Base64编码的HMAC-SHA256签名
     * @throws RuntimeException 当算法不支持或密钥无效时抛出
     */
    public String calculateHmac(String key, String data) throws Exception {
        return this.hmacComponent.calculate(key, data);
    }
}
