package com.atlassian.stash.internal.web;

import com.atlassian.stash.internal.web.auth.IgnoresXsrf;
import com.atlassian.stash.internal.web.soy.StashSoyResponseBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;

@Controller
public class ErrorPageController {

    public static final String ATTRIBUTE_EXCEPTION = "javax.servlet.error.exception";
    public static final String ATTRIBUTE_REQUEST_URI = "javax.servlet.error.request_uri";

    private static final Logger log = LoggerFactory.getLogger(ErrorPageController.class);

    /**
     * This controller handles the error page defined in the web.xml. This is a Catch all Unauthorized handler
     * @param request used to retrieve the URL for which the 401 occurred
     * @return model and view
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @RequestMapping("/error401")
    @IgnoresXsrf
    public ModelAndView handle401(HttpServletRequest request) {
        Object requestUri = request.getAttribute(ATTRIBUTE_REQUEST_URI);
        log.debug("User is not authorized to access [{}]", requestUri);

        return createModelAndView(request, "bitbucket.internal.errors.error401");
    }

    /**
     * This controller handles the error page defined in the web.xml. This is a Catch all PageNotFound handler
     * @param request used to retrieve the URL for which the 404 occurred
     * @return model and view
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @RequestMapping("/error404")
    @IgnoresXsrf
    public ModelAndView handle404(HttpServletRequest request) {
        Object requestUri = request.getAttribute(ATTRIBUTE_REQUEST_URI);
        log.debug("Page not found attempting to access [{}]", requestUri);

        return createModelAndView(request, "bitbucket.internal.errors.error404");
    }

    /**
     * This controller handles the error page defined in the web.xml. This is a Catch all ServerError handler
     * @param request used to retrieve the URL for which the 500 occurred, and the exception if available
     * @return model and view
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @RequestMapping("/error500")
    @IgnoresXsrf
    public ModelAndView handle500(HttpServletRequest request) {
        Object requestUri = request.getAttribute(ATTRIBUTE_REQUEST_URI);
        Object exception = request.getAttribute(ATTRIBUTE_EXCEPTION);
        if (exception instanceof Throwable) {
            log.error("There was an unhandled exception loading [" + requestUri + "]", (Throwable) exception);
        } else {
            log.error("There was an unhandled error loading [{}]", requestUri);
        }

        return new ModelAndView("bitbucket.internal.errors.error500");
    }

    private ModelAndView createModelAndView(HttpServletRequest request, String template) {
        StashSoyResponseBuilder responseBuilder = new StashSoyResponseBuilder(template);
        Object error = request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
        if (error instanceof String) {
            responseBuilder.put("errorHeadingOverride", error);
        }
        return responseBuilder.build();
    }
}
