package de.effigenix.infrastructure.security;

import de.effigenix.domain.usermanagement.Permission;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

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

/**
 * JWT Authentication Filter for Spring Security.
 *
 * Intercepts HTTP requests and validates JWT tokens from the Authorization header.
 * If valid, sets the Authentication in Spring SecurityContext.
 *
 * Flow:
 * 1. Extract JWT token from Authorization header (Bearer <token>)
 * 2. Validate token signature and expiration
 * 3. Extract userId, username, permissions, branchId from token claims
 * 4. Create Authentication object with permissions as GrantedAuthorities
 * 5. Set Authentication in SecurityContext
 *
 * This filter runs BEFORE Spring Security's authentication filters.
 * It's registered in SecurityConfig via addFilterBefore().
 *
 * Infrastructure Layer → Spring Security Filter Chain
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_PREFIX = "Bearer ";

    private final JwtTokenProvider tokenProvider;
    private final JwtSessionManager sessionManager;

    public JwtAuthenticationFilter(JwtTokenProvider tokenProvider, JwtSessionManager sessionManager) {
        this.tokenProvider = tokenProvider;
        this.sessionManager = sessionManager;
    }

    /**
     * Filters every HTTP request to validate JWT tokens.
     *
     * @param request HTTP request
     * @param response HTTP response
     * @param filterChain Filter chain to continue processing
     */
    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain
    ) throws ServletException, IOException {

        try {
            // Extract JWT token from Authorization header
            String token = extractTokenFromRequest(request);

            if (token != null) {
                // Validate token (signature, expiration, blacklist)
                sessionManager.validateToken(token);

                // Extract user information from token
                String userId = tokenProvider.extractUserId(token).value();
                String username = tokenProvider.extractUsername(token);
                Set<Permission> permissions = tokenProvider.extractPermissions(token);
                String branchId = tokenProvider.extractBranchId(token);

                // Convert permissions to Spring Security GrantedAuthorities
                var authorities = permissions.stream()
                        .map(p -> new SimpleGrantedAuthority(p.name()))
                        .collect(Collectors.toSet());

                // Create Authentication object
                // Principal: userId (used by AuthorizationPort.currentActor())
                // Credentials: null (stateless - no password needed)
                // Authorities: user's permissions
                var authentication = new UsernamePasswordAuthenticationToken(
                        userId,
                        null,
                        authorities
                );

                // Store additional details (username, branchId) for resource-level authorization
                var details = new JwtAuthenticationDetails(username, branchId);
                authentication.setDetails(details);

                // Set authentication in SecurityContext
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }

        } catch (JwtException | SecurityException | IllegalArgumentException e) {
            // Token validation failed - clear SecurityContext and continue
            // Spring Security will return 401 Unauthorized for protected endpoints
            SecurityContextHolder.clearContext();

            logger.warn("JWT authentication failed: " + e.getMessage());
        }

        // Continue filter chain (even if authentication failed)
        filterChain.doFilter(request, response);
    }

    /**
     * Extracts JWT token from the Authorization header.
     *
     * Expected format: "Authorization: Bearer <token>"
     *
     * @param request HTTP request
     * @return JWT token or null if not present
     */
    private String extractTokenFromRequest(HttpServletRequest request) {
        String authHeader = request.getHeader(AUTHORIZATION_HEADER);

        if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) {
            return authHeader.substring(BEARER_PREFIX.length());
        }

        return null;
    }
}
