package com.ttstd.signaling.config;

import com.ttstd.signaling.model.AuthPrincipal;
import com.ttstd.signaling.security.AuthException;
import com.ttstd.signaling.service.AccountService;
import com.ttstd.signaling.service.DeviceIdentityService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

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

/**
 * Bearer 令牌解析过滤器。
 *
 * <p>解析 {@code Authorization: Bearer <token>} 并将 {@link AuthPrincipal} 写入请求属性，
 * 供受保护接口读取。对无需认证的公开端点直接放行；
 * 对需要认证但令牌无效的请求返回 401。
 */
@Component
@Order(1)
public class BearerAuthFilter extends OncePerRequestFilter {

    public static final String ATTR_PRINCIPAL = "authPrincipal";

    /** 需要令牌的受保护端点前缀（用户或设备令牌均可） */
    private static final Set<String> PROTECTED_PREFIXES = Set.of("/api/auth/", "/api/client/");

    private final AccountService accountService;
    private final DeviceIdentityService deviceIdentityService;

    public BearerAuthFilter(AccountService accountService,
                            DeviceIdentityService deviceIdentityService) {
        this.accountService = accountService;
        this.deviceIdentityService = deviceIdentityService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String uri = request.getRequestURI();

        if (PublicEndpoints.isPublic(request)) {
            filterChain.doFilter(request, response);
            return;
        }

        String token = extractBearer(request);
        if (token != null) {
            AuthPrincipal principal = resolve(token);
            if (principal != null) {
                request.setAttribute(ATTR_PRINCIPAL, principal);
            }
        }

        boolean requiresAuth = PROTECTED_PREFIXES.stream().anyMatch(uri::startsWith);
        if (requiresAuth && request.getAttribute(ATTR_PRINCIPAL) == null) {
            writeUnauthorized(response);
            return;
        }

        filterChain.doFilter(request, response);
    }

    /** 依次尝试用户令牌与设备令牌。 */
    private AuthPrincipal resolve(String token) {
        try {
            return accountService.authenticate(token);
        } catch (AuthException ignored) {
            try {
                return deviceIdentityService.authenticate(token);
            } catch (AuthException ignored2) {
                return null;
            }
        }
    }

    private String extractBearer(HttpServletRequest request) {
        String authorization = request.getHeader("Authorization");
        if (authorization != null && authorization.regionMatches(true, 0, "Bearer ", 0, 7)) {
            String token = authorization.substring(7).trim();
            return token.isEmpty() ? null : token;
        }
        return null;
    }

    private void writeUnauthorized(HttpServletResponse response) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("{\"code\":401,\"message\":\"认证失败\"}");
    }
}
