package com.pharmacopoeia.security;

import com.pharmacopoeia.config.AuthProperties;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import com.pharmacopoeia.util.IpUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;
import java.util.Set;

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    private static final Logger log = LoggerFactory.getLogger(JwtAuthFilter.class);

    /**
     * 始终公开的路径（不需要 token）
     */
    private static final Set<String> PUBLIC_PREFIXES = Set.of(
            "/health",
            "/api/v1/auth/",
            "/api/v1/yaodian/",
            "/api/v1/analytics/",
            "/static/",
            "/favicon.ico"
    );

    private final JwtUtil jwtUtil;
    private final AuthProperties authProperties;

    public JwtAuthFilter(JwtUtil jwtUtil, AuthProperties authProperties) {
        this.jwtUtil = jwtUtil;
        this.authProperties = authProperties;
    }

    /**
     * 异步分发（如 SSE 流式完成后 Tomcat 的 AsyncContext dispatch）也需要重新设置
     * SecurityContext，否则 AuthorizationFilter 会因找不到认证信息而抛出
     * AccessDeniedException。
     */
    @Override
    protected boolean shouldNotFilterAsyncDispatch() {
        return false;
    }

    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest request,
                                    @NotNull HttpServletResponse response,
                                    @NotNull FilterChain filterChain) throws ServletException, IOException {
        String ip = IpUtils.getClientIp(request);
        MDC.put("ip", ip);
        try {
            String uri = request.getRequestURI();
            String method = request.getMethod();

            // 无强鉴权：有 token 则解析身份，无 token 也直接放行
            filterChain.doFilter(request, response);
            return;
        }finally {
            MDC.clear();
        }
    }

    private boolean isPublicPath(String uri) {
        for (String prefix : PUBLIC_PREFIXES) {
            if (uri.startsWith(prefix)) {
                return true;
            }
        }
        return "/".equals(uri) || uri.endsWith(".html") || uri.endsWith(".js") || uri.endsWith(".css");
    }
    private void send401(HttpServletResponse response, String msg) throws IOException {
        response.setStatus(401);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("{\"error\":\"" + msg + "\",\"code\":401}");
    }
}