package com.mzh.library.filter;

import com.mzh.library.util.SessionAttributes;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.Filter;
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 javax.servlet.http.HttpSession;

public class AuthenticationFilter implements Filter {
    private static final Set<String> PUBLIC_PATHS = new HashSet<>(Arrays.asList(
        "",
        "/",
        "/login",
        "/unauthorized",
        "/favicon.ico"
    ));

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        String path = relativePath(httpRequest);

        if (isPublic(path)) {
            chain.doFilter(request, response);
            return;
        }

        HttpSession session = httpRequest.getSession(false);
        Object user = session == null ? null : session.getAttribute(SessionAttributes.AUTHENTICATED_USER);
        if (user != null) {
            chain.doFilter(request, response);
            return;
        }

        String redirect = encode(buildRelativeUrl(httpRequest, path));
        httpResponse.sendRedirect(httpRequest.getContextPath() + "/login?redirect=" + redirect);
    }

    private boolean isPublic(String path) {
        return PUBLIC_PATHS.contains(path)
            || path.startsWith("/static/");
    }

    private String relativePath(HttpServletRequest request) {
        String contextPath = request.getContextPath();
        String requestUri = request.getRequestURI();
        return requestUri.substring(contextPath.length());
    }

    private String buildRelativeUrl(HttpServletRequest request, String path) {
        String query = request.getQueryString();
        return query == null || query.isEmpty() ? path : path + "?" + query;
    }

    private String encode(String value) throws UnsupportedEncodingException {
        return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
    }
}
