package com.malk.filter;

import com.alibaba.fastjson.JSON;
import com.malk.config.AuthConfigProperties;
import com.malk.server.common.McR;
import com.malk.utils.UtilSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;

/**
 * 请求签名校验拦截器（capability: request-auth 第二阶段）
 *
 * <p>接续 {@link AuthFilter} 完成最后一步：签名校验。本 Interceptor 在 Spring MVC
 * 派发之前、Controller 之后（{@code preHandle}），此时能访问到 {@link HandlerMethod}
 * 判断 {@link NoAuth} 注解。</p>
 */
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {

    private static final Logger log = LoggerFactory.getLogger("point");
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Autowired
    private AuthConfigProperties props;

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        if (!props.isEnabled()) return true;
        if (isExempt(request.getServletPath())) return true;

        // @NoAuth 豁免（类级或方法级）
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            if (hm.getMethodAnnotation(NoAuth.class) != null
                    || hm.getBeanType().isAnnotationPresent(NoAuth.class)) {
                return true;
            }
        }

        if (props.getSecret() == null || props.getSecret().isEmpty()) {
            write(response, 503, "AUTH_CONFIG_MISSING");
            log.error("[Auth] secret 未配置但 enabled=true，拒绝请求 path={}", request.getServletPath());
            return false;
        }

        String ts = request.getHeader(AuthFilter.H_TS);
        String nonce = request.getHeader(AuthFilter.H_NONCE);
        String sig = request.getHeader(AuthFilter.H_SIG);

        String body = readBody(request);
        String bodyHash = UtilSignature.sha256Hex(body.getBytes(StandardCharsets.UTF_8));
        String expected = UtilSignature.sign(props.getSecret(), ts, nonce,
                request.getMethod().toUpperCase(), request.getServletPath(), bodyHash);

        if (!UtilSignature.safeEquals(sig, expected)) {
            write(response, 403, "AUTH_SIGNATURE_INVALID");
            log.warn("[Auth] signature mismatch path={} key={}",
                    request.getServletPath(), request.getHeader(AuthFilter.H_KEY));
            return false;
        }
        return true;
    }

    private boolean isExempt(String path) {
        if (props.getExemptPaths() == null) return false;
        for (String pat : props.getExemptPaths()) {
            if (pathMatcher.match(pat, path)) return true;
        }
        return false;
    }

    private static String readBody(HttpServletRequest req) throws java.io.IOException {
        // 注意：原始 HttpServletRequest 的 body 只能读一次。调用方若需要在 Controller 里再读，
        // 需要使用 ContentCachingRequestWrapper（由 Spring MVC 提供，建议配合 Filter 包装）。
        // 本实现优先保证签名校验可行；body 二次读取的包装由调用方在应用层处理。
        BufferedReader reader = req.getReader();
        if (reader == null) return "";
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        return sb.toString();
    }

    private static void write(HttpServletResponse resp, int status, String code) throws java.io.IOException {
        resp.setStatus(status);
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset=UTF-8");
        McR r = McR.error(code, code);
        resp.getOutputStream().write(JSON.toJSONString(r).getBytes(StandardCharsets.UTF_8));
    }
}
