package com.atlassian.bitbucket.internal.tag.rest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * A Servlet Filter that provides URL forwarding and, optionally, the addition of a deprecation header on the
 * HTTP response.
 * <p>
 * Three configuration parameters are available, and are provided to the filter via its init() method:
 * <ul>
 *     <li>fromPath - The path to forward from. If the relative path does not start with this string then no
 *     forwarding will occur</li>
 *     <li>toPath - The path to forward to. The forward URL will have the "fromPath" replaced with the "toPath"</li>
 *     <li>deprecationMessage - (Optional) A string that will be added to the response header</li>
 * </ul>
 * <p>
 * Note that both the fromPath and toPath must occur at the start of the relative URL. By way of a practical example,
 * the relative URL is defined as: given the full URL "http://www.example.com/bitbucket/rest...", the relative URL
 * will be "/rest...".
 */
public class DeprecatedPathFilter implements Filter {
    private static final String DEPRECATION_HEADER_KEY = "Atl-API-Deprecated";
    private static final String DEPRECATION_PARAM_NAME = "deprecationMessage";
    private static final String FROM_PATH_PARAM_NAME = "fromPath";
    private static final String TO_PATH_PARAM_NAME = "toPath";
    private static final Logger log = LoggerFactory.getLogger(DeprecatedPathFilter.class);

    private String deprecationMessage;
    private String fromPath;
    private String toPath;

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (!isPathValid(fromPath) || !isPathValid(toPath)) {
            log.error("Invalid redirect path configuration: " + fromPath + " -> " + toPath);
            chain.doFilter(request, response);
            return;
        }

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String relativePath = getRelativePath(httpRequest);

        if (relativePath.startsWith(fromPath)) {
            if (deprecationMessage != null) {
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                httpResponse.addHeader(DEPRECATION_HEADER_KEY, deprecationMessage);
            }

            String forwardUrl = relativePath.replaceFirst("^" + fromPath, toPath);
            ServletContext context = httpRequest.getServletContext();
            context.getRequestDispatcher(forwardUrl).forward(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        deprecationMessage = filterConfig.getInitParameter(DEPRECATION_PARAM_NAME);
        fromPath = filterConfig.getInitParameter(FROM_PATH_PARAM_NAME);
        toPath = filterConfig.getInitParameter(TO_PATH_PARAM_NAME);
    }

    /**
     * Returns the relative request path. For example, given a full path of "http://www.example.com/bitbucket/rest..."
     * this will return "/rest...".
     */
    private String getRelativePath(HttpServletRequest request) {
        return request.getRequestURI().substring(request.getContextPath().length());
    }

    /**
     * Validates the path parameters that are provided via init()
     */
    private boolean isPathValid(String path) {
        return path != null && !path.isEmpty() && path.charAt(0) == '/';
    }
}
