package com.spring.infra.security.filter;

import java.io.IOException;
import java.util.Objects;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.infra.security.config.PermittedURI;
import com.spring.infra.security.domain.UserPrincipal;
import com.spring.infra.security.dto.SignInRequest;
import com.spring.infra.security.error.SecurityAuthException;
import com.spring.infra.security.error.SecurityExceptionRule;

public class AuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    private static final String DEFAULT_LOGIN_REQUEST_URI = PermittedURI.USER_SIGN_IN.getUri();
	private static final String HTTP_METHOD = "POST"; 
    private static final AntPathRequestMatcher DEFAULT_LOGIN_PATH_REQUEST_MATCHER = new AntPathRequestMatcher(DEFAULT_LOGIN_REQUEST_URI, HTTP_METHOD);

    private final ObjectMapper objectMapper;

    public AuthenticationProcessingFilter(ObjectMapper objectMapper) {
        super(DEFAULT_LOGIN_PATH_REQUEST_MATCHER);
        this.objectMapper = objectMapper;
    }

    @Override
    public Authentication attemptAuthentication(
        HttpServletRequest request, 
        HttpServletResponse response
    ) throws AuthenticationException, IOException, ServletException {

        if (!isValidRequestType(request)) {
            throw new SecurityAuthException(SecurityExceptionRule.UNSUPPORTED_MEDIA_ERROR);
        }
        var signInRequest = objectMapper.readValue(request.getReader(), SignInRequest.class);
        if (!isValidRequest(signInRequest)) {
            throw new SecurityAuthException(SecurityExceptionRule.USER_BAD_REQUEST);
        }
		var token = new UsernamePasswordAuthenticationToken(signInRequest.getUsername(), signInRequest.getPassword());
        var authentication = this.getAuthenticationManager().authenticate(token);

        if (authentication.getPrincipal() instanceof UserPrincipal) {
            UserPrincipal user = (UserPrincipal) authentication.getPrincipal();
            if (!user.getAgentUser().isApproved()) {
                throw new SecurityAuthException(SecurityExceptionRule.USER_NOT_APPROVED);
            }
        }

        return authentication;
    }

    private boolean isValidRequestType(HttpServletRequest request) {
        return Objects.equals(request.getMethod(), HttpMethod.POST.name()) &&
                Objects.equals(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
    }

    private boolean isValidRequest(SignInRequest signInRequest) {
        return StringUtils.hasText(signInRequest.getUsername()) || 
                StringUtils.hasText(signInRequest.getPassword());
    }
    
}
