package cloud.nstart.terravault.ordinis.restapi.auth;

import cloud.nstart.terravault.ordinis.restapi.service.UserDisplayService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * Capture-only filter: на каждом authenticated HTTP запросе достаёт
 * {@code sub} / {@code preferred_username} / {@code name} / {@code email}
 * из JWT и upsert'ит в {@link UserDisplayService}. Это позволяет резолвить
 * UUID → display name для UserCell на frontend без Keycloak Admin API call.
 *
 * <p>Runs после Spring Security JWT processing (он создаёт
 * {@code JwtAuthenticationToken} в SecurityContext). Если auth.token = null
 * (anonymous request, или non-OIDC auth), filter — no-op.
 *
 * <p>Cost: 1 hash put per authenticated request. Cache size capped в
 * service до 10k entries → ~1MB max RAM.
 */
@Component
public class JwtUserCaptureFilter extends OncePerRequestFilter {

  private final UserDisplayService displayService;

  public JwtUserCaptureFilter(UserDisplayService displayService) {
    this.displayService = displayService;
  }

  @Override
  protected void doFilterInternal(
      HttpServletRequest request,
      HttpServletResponse response,
      FilterChain chain) throws ServletException, IOException {
    captureIfJwt();
    chain.doFilter(request, response);
  }

  private void captureIfJwt() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (!(auth instanceof JwtAuthenticationToken jwt)) return;
    Jwt token = jwt.getToken();
    String sub = token.getSubject();
    if (sub == null || sub.isBlank()) return;
    String preferredUsername = token.getClaimAsString("preferred_username");
    String name = token.getClaimAsString("name");
    String email = token.getClaimAsString("email");
    displayService.put(sub, preferredUsername, name, email);
  }
}
