package com.ffii.core.security.filter;

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

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ffii.core.security.authentication.AuthToken;
import com.ffii.core.utils.StringUtils;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;

/**
 * Authentication Token Filter
 *
 * @author Patrick
 */
public class AuthTokenFilter extends AbstractAuthenticationProcessingFilter {

    public static final String TOKEN_HEADER = "x-auth-token";

    public AuthTokenFilter(RequestMatcher requestMatcher) {
        super(requestMatcher);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        final String tokenValue = getTokenValue((HttpServletRequest) request);

        // This filter only applies if the header is present
        if (StringUtils.isEmpty(tokenValue)) {
            chain.doFilter(request, response);
            return;
        }

        // On success keep going on the chain
        this.setAuthenticationSuccessHandler((request1, response1, authentication) -> {
            chain.doFilter(request1, response1);
        });

        super.doFilter(request, response, chain);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
        final String tokenValue = getTokenValue(request);

        if (StringUtils.isEmpty(tokenValue)) {
            return null;
        }

        AuthToken token = new AuthToken(tokenValue);
        token.setDetails(authenticationDetailsSource.buildDetails(request));

        return this.getAuthenticationManager().authenticate(token);
    }

    private String getTokenValue(HttpServletRequest request) {
        return Collections.list(request.getHeaderNames()).stream()
                .filter(header -> TOKEN_HEADER.equalsIgnoreCase(header)).map(header -> request.getHeader(header))
                .findFirst().orElse(null);
    }

}