package com.spring.common.support;

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.spring.common.converter.CommHttpMessageConverter;
import com.spring.common.error.ErrorResponse;
import com.spring.common.error.ErrorRule;
import com.spring.common.error.GlobalExceptionHandler;
import com.spring.common.validation.CollectionValidator;
import com.spring.infra.security.error.SecurityExceptionHandler;

@RestControllerAdvice(
    basePackages = "com.spring.domain",
    basePackageClasses = { GlobalExceptionHandler.class, SecurityExceptionHandler.class }
)
public class ResponseWrapper implements ResponseBodyAdvice<Object> {

    private final LocalValidatorFactoryBean validator;

    public ResponseWrapper(LocalValidatorFactoryBean validator) {
        this.validator = validator;
    }

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.addValidators(new CollectionValidator(validator));
    }
    
    @Override
    public boolean supports(
        @NonNull MethodParameter returnType, 
        @NonNull Class<? extends HttpMessageConverter<?>> converterType
    ) {
        return CommHttpMessageConverter.class.isAssignableFrom(converterType);
    }

    @Override
    public Object beforeBodyWrite(
        @Nullable Object body,
        @NonNull MethodParameter returnType,
        @NonNull MediaType selectedContentType,
        @NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
        @NonNull ServerHttpRequest request,
        @NonNull ServerHttpResponse response
    ) {
        String path = request.getURI().getPath();

        if (body instanceof ErrorResponse) {
            ErrorResponse errorData = ((ErrorResponse) body);
            ErrorRule errorRule = errorData.getErrorRule();
            response.setStatusCode(errorRule.getStatus());

            return ApiResponse.builder()
                .statusCode(errorRule.getStatus().value())
                .path(path)
                .data(errorData.getErrors())
                .message(errorRule.getMessage())
                .build();
        }

        return ApiResponse.builder()
            .statusCode(HttpStatus.OK.value())
            .path(path)
            .data(body)
            .build();
    }

}
