package com.ksoichiro.task.web;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

@ControllerAdvice
@Controller
@Aspect
@Slf4j
public class AppErrorController implements ErrorController {
    @Autowired
    private ErrorAttributes errorAttributes;

    @Autowired
    private MessageSource messageSource;

    @Pointcut("@within(org.springframework.stereotype.Controller)")
    public void controller() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void requestMapping() {
    }

    @Pointcut("execution(* com.ksoichiro.task.web..*.*(..))")
    public void anyProjectMethodExecution() {
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }

    @Around("controller() && requestMapping() && anyProjectMethodExecution()")
    public Object controllerExecution(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        Object result = pjp.proceed();
        log.info("Processed request {}", request.getRequestURI());
        return result;
    }

    // This won't be called
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public String handleNotFoundError(Throwable throwable, Model model) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        log.error("Not found {}", request.getRequestURI());
        Map<String, Object> attrs = new HashMap<>();
        attrs.put("status", 404);
        attrs.put("error", "Not Found");
        model.addAttribute("error", attrs);
        return "error";
    }

    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public String handleException(Throwable throwable, Model model) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        log.error("Unhandled exception in {}: {}", request.getRequestURI(), throwable.toString(), throwable);
        Map<String, Object> attrs = new HashMap<>();
        attrs.put("status", 500);
        attrs.put("error", "Internal Server Error");
        model.addAttribute("error", attrs);
        model.addAttribute("guidance", messageSource.getMessage("http.error.500", null, Locale.getDefault()));
        return "error";
    }

    @RequestMapping("/error")
    public String error(Model model) {
        Map<String, Object> attrs = errorAttributes.getErrorAttributes(RequestContextHolder.currentRequestAttributes(), true);
        model.addAttribute("error", attrs);
        // Try to add an additional guidance message for specific status
        try {
            Integer status = (Integer) attrs.get("status");
            if (status == 404) {
                // TODO With this implementation, the original requested URI is now shown.
                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
                log.info("Unknown URI (404): {}", request.getRequestURI());
            }
            model.addAttribute("guidance", messageSource.getMessage("http.error." + status, null, Locale.getDefault()));
        } catch (NoSuchMessageException ignore) {
        }

        return "error";
    }
}

@@ -590,57 +590,8 @@
 us;%0A
-import org.springframework.stereotype.Component;%0A
 impo
@@ -628,32 +628,32 @@
 ype.Controller;%0A
+
 import org.sprin
@@ -1074,72 +1074,8 @@
 tes;
-%0Aimport org.springframework.web.servlet.NoHandlerFoundException;
 %0A%0Aim
@@ -2274,604 +2274,8 @@
  %7D%0A%0A
-    // This won't be called%0A    @ResponseStatus(value = HttpStatus.NOT_FOUND)%0A    @ExceptionHandler(NoHandlerFoundException.class)%0A    public String handleNotFoundError(Throwable throwable, Model model) %7B%0A        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();%0A        log.error(%22Not found %7B%7D%22, request.getRequestURI());%0A        Map%3CString, Object%3E attrs = new HashMap%3C%3E();%0A        attrs.put(%22status%22, 404);%0A        attrs.put(%22error%22, %22Not Found%22);%0A        model.addAttribute(%22error%22, attrs);%0A        return %22error%22;%0A    %7D%0A%0A
     
@@ -3445,291 +3445,50 @@
     
-// TODO With this implementation, the original requested URI is now shown.%0A                HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();%0A                log.info(%22Unknown URI (404): %7B%7D%22, request.getRequestURI(
+log.info(%22Not found: %7B%7D%22, attrs.get(%22path%22
 ));%0A

Fix 404 handling