package com.huimv.employment.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.huimv.employment.common.exception.BizException;
import com.huimv.employment.common.exception.ErrorCode;
import com.huimv.employment.common.web.R;
import com.huimv.employment.service.auth.AuthUserValidator;
import com.huimv.employment.service.auth.JwtTokenService;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * JWT 鉴权过滤器：保护 /api/v1/mp/** 接口（auth、health 等匿名路径除外）。
 * <p>解析 JWT 后查库校验账号状态，并以数据库 {@code user_type} 作为鉴权身份。</p>
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 20)
public class JwtAuthFilter extends OncePerRequestFilter {

    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    /** 无需 JWT 的 mp 接口路径（账密 set/status 需登录） */
    private static final String[] ANONYMOUS_PATTERNS = {
            "/api/v1/mp/auth/sms/**",
            "/api/v1/mp/auth/wechat/**",
            "/api/v1/mp/auth/password/login",
            "/api/v1/mp/auth/password/reset",
            "/api/v1/mp/auth/password/public-key",
            "/api/v1/mp/health"
    };

    private final JwtTokenService jwtTokenService;
    private final AuthUserValidator authUserValidator;
    private final ObjectMapper objectMapper;

    public JwtAuthFilter(JwtTokenService jwtTokenService,
                         AuthUserValidator authUserValidator,
                         ObjectMapper objectMapper) {
        this.jwtTokenService = jwtTokenService;
        this.authUserValidator = authUserValidator;
        this.objectMapper = objectMapper;
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String path = request.getRequestURI();
        if (!path.startsWith("/api/v1/mp/")) {
            return true;
        }
        for (String pattern : ANONYMOUS_PATTERNS) {
            if (PATH_MATCHER.match(pattern, path)) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            String authorization = request.getHeader("Authorization");
            if (!StringUtils.hasText(authorization) || !authorization.startsWith("Bearer ")) {
                writeUnauthorized(response);
                return;
            }
            JwtTokenService.TokenClaims claims = jwtTokenService.parseToken(authorization.substring(7));
            AuthUserValidator.ValidatedUser user = authUserValidator.validate(claims.getUserId());
            LoginUserHolder.set(new LoginUser(user.getUserId(), user.getUserType()));
            filterChain.doFilter(request, response);
        } catch (BizException ex) {
            writeError(response, ex.getCode(), ex.getMessage());
        } finally {
            LoginUserHolder.clear();
        }
    }

    private void writeUnauthorized(HttpServletResponse response) throws IOException {
        writeError(response, ErrorCode.UNAUTHORIZED.getCode(), ErrorCode.UNAUTHORIZED.getMessage());
    }

    private void writeError(HttpServletResponse response, int code, String message) throws IOException {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.getWriter().write(objectMapper.writeValueAsString(R.fail(code, message)));
    }
}
