package be.smals.morst.training.service.handler;

import io.github.belgif.rest.problem.BadRequestProblem;
import io.github.belgif.rest.problem.api.InEnum;
import io.github.belgif.rest.problem.api.InputValidationIssue;
import io.github.belgif.rest.problem.api.InputValidationIssues;
import io.github.belgif.rest.problem.api.Problem;
import io.github.belgif.rest.problem.spring.ProblemMediaType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.method.ParameterValidationResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.HandlerMethodValidationException;

import java.lang.annotation.Annotation;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestControllerAdvice
@ConditionalOnClass({HandlerMethodValidationException.class})
@Order(1)
public class HandlerMethodValidationExceptionHandler {

    @ExceptionHandler({HandlerMethodValidationException.class})
    public ResponseEntity<Problem> handleConstraintViolationException(HandlerMethodValidationException exception) {
        return ProblemMediaType.INSTANCE.toResponse(new BadRequestProblem(exception.getParameterValidationResults().stream().flatMap(HandlerMethodValidationExceptionHandler::convertToInputValidationIssue).sorted(InputValidationIssue.BY_NAME).collect(Collectors.toList())));
    }

    public static Stream<InputValidationIssue> convertToInputValidationIssue(ParameterValidationResult result) {
        MethodParameter methodParameter = result.getMethodParameter();
        Annotation[] annotations = methodParameter.getParameterAnnotations();

        InEnum in = null;
        String name = null;
        for (Annotation annotation : annotations) {
            in = switch (annotation) {
                case PathVariable ignored -> InEnum.PATH;
                case RequestParam ignored -> InEnum.QUERY;
                case RequestHeader ignored -> InEnum.HEADER;
                case null, default -> InEnum.BODY;
            };
            name = switch (annotation) {
                case PathVariable pathVariable -> pathVariable.name();
                case RequestParam requestParam -> requestParam.name();
                case RequestHeader requestHeader -> requestHeader.name();
                case null, default -> null;
            };
        }
        InEnum _in = in;
        String _name = name;
        return result.getResolvableErrors().stream().map(error -> InputValidationIssues.schemaViolation(_in, _name, result.getArgument(), error.getDefaultMessage()));
    }

}
