package egovframework.com.its.web.server.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;

@Slf4j
@ControllerAdvice
public class ExceptionContollerAdvisor {
    private String fieldName2JsonName(String str){
        boolean isFound = false;
        StringBuilder sb = new StringBuilder();
        for (char x : str.toCharArray()) {
            if (Character.isUpperCase(x)) {
                sb.append("_").append(Character.toLowerCase(x));
                isFound = true;
            } else {
                sb.append(x);
            }
        }
        return isFound ? sb.toString() : "";
    }

    /**
     * Validation error
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> methodValidException(HttpServletRequest request, MethodArgumentNotValidException ex) {
        List<String> errorList = new ArrayList<>();
        BindingResult bindingResult = ex.getBindingResult();
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            StringBuilder builder = new StringBuilder();
            String fieldName = fieldName2JsonName(fieldError.getField());
            if ("".equals(fieldName)) {
                fieldName = fieldError.getField();
            } else {
                fieldName = fieldError.getField() + "(" + fieldName + ")";
            }

            builder.append("[");
            builder.append(fieldName);
            builder.append("](은)는 ");
            builder.append(fieldError.getDefaultMessage());
            builder.append(" 입력된 값: [");
            builder.append(fieldError.getRejectedValue());
            builder.append("]");
            errorList.add(builder.toString());
        }
//        List<String> errorList = ex
//                .getBindingResult()
//                .getFieldErrors()
//                .stream()
//                .map(DefaultMessageSourceResolvable::getDefaultMessage)
//                .collect(Collectors.toList());
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.BAD_REQUEST.value())
                .title("Arguments Not Valid(Bad Request)")
                .message("요청 데이터가 유효하지 않습니다.")
                .errors(errorList)
                .path(request.getRequestURI())
                .build();
        log.error("{}", response.toString());
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }

    /**
     * Data not found
     * @param request
     * @param ex
     * @return
     */
    @ExceptionHandler(NoSuchElementException.class)
    public ResponseEntity<ErrorResponse> exceptionHandler(HttpServletRequest request, final NoSuchElementException ex) {
        List<String> errorList = new ArrayList<>(Collections.singletonList(ex.getMessage()));
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.NOT_FOUND.value())
                .title("Data Not Found")
                .message(ex.getMessage())
                .path(request.getRequestURI())
                .errors(errorList)
                .build();
        log.error("{}", response.toString());
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(NumberFormatException.class)
    public ResponseEntity<ErrorResponse> numberFormatExceptionHandler(HttpServletRequest request, final NumberFormatException ex) {
        List<String> errorList = new ArrayList<>(Collections.singletonList(ex.getMessage()));
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.EXPECTATION_FAILED.value())
                .title("NumberFormatException")
                .message(ex.getMessage())
                .path(request.getRequestURI())
                .errors(errorList)
                .build();
        log.error("{}", response.toString());
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ErrorResponse> exceptionHandler(HttpServletRequest request, final RuntimeException ex) {
        List<String> errorList = new ArrayList<>(Collections.singletonList(ex.getMessage()));
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.EXPECTATION_FAILED.value())
                .title("RuntimeException")
                .message(ex.getMessage())
                .path(request.getRequestURI())
                .errors(errorList)
                .build();
        log.error("{}", response.toString());
        return new ResponseEntity<>(response, HttpStatus.EXPECTATION_FAILED);
    }

    @ExceptionHandler(IllegalStateException.class)
    public ResponseEntity<ErrorResponse> exceptionHandler(HttpServletRequest request, final IllegalStateException ex) {
        List<String> errorList = new ArrayList<>(Collections.singletonList(ex.getMessage()));
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.EXPECTATION_FAILED.value())
                .title("IllegalStateException")
                .message(ex.getMessage())
                .path(request.getRequestURI())
                .errors(errorList)
                .build();
        log.error("illegalStateExceptionHandler : {}", response.toString());
        return new ResponseEntity<>(response, HttpStatus.EXPECTATION_FAILED);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> exceptionHandler(HttpServletRequest request, final Exception ex) {
        List<String> errorList = new ArrayList<>(Collections.singletonList(ex.getMessage()));
        ErrorResponse response = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(HttpStatus.EXPECTATION_FAILED.value())
                .title("Exception")
                .message(ex.getMessage())
                .path(request.getRequestURI())
                .errors(errorList)
                .build();
        log.error("{}", response.toString());
        return new ResponseEntity<>(response, HttpStatus.EXPECTATION_FAILED);
    }
}
