package com.starry.common.advice;

import com.starry.common.interceptor.RequestLoggingInterceptor;
import com.starry.common.result.R;
import com.starry.common.result.TypedR;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * Captures business result metadata from standard API wrappers so the
 * request logging interceptor can log based on business outcome rather than
 * just the HTTP status code.
 */
@ControllerAdvice
public class BusinessResultCaptureAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(@Nullable MethodParameter returnType,
                            @Nullable Class<? extends HttpMessageConverter<?>> converterType) {
        // Inspect every response; we'll short-circuit in beforeBodyWrite if the body isn't one we care about.
        return true;
    }

    @Override
    public Object beforeBodyWrite(@Nullable Object body,
                                  @Nullable MethodParameter returnType,
                                  @Nullable MediaType selectedContentType,
                                  @Nullable Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  @Nullable ServerHttpRequest request,
                                  @Nullable ServerHttpResponse response) {

        Object candidate = extractBody(body);

        RequestLoggingInterceptor.BusinessResult businessResult = null;
        if (candidate instanceof R) {
            R r = (R) candidate;
            businessResult = new RequestLoggingInterceptor.BusinessResult(r.isSuccess(), r.getCode(), r.getMessage());
        } else if (candidate instanceof TypedR) {
            TypedR<?> typedR = (TypedR<?>) candidate;
            businessResult = new RequestLoggingInterceptor.BusinessResult(typedR.isSuccess(), typedR.getCode(), typedR.getMessage());
        }

        if (businessResult != null && request instanceof ServletServerHttpRequest) {
            HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
            servletRequest.setAttribute(RequestLoggingInterceptor.BUSINESS_RESULT_ATTRIBUTE, businessResult);
        }

        return body;
    }

    @Nullable
    private Object extractBody(@Nullable Object body) {
        if (body instanceof ResponseEntity<?>) {
            return ((ResponseEntity<?>) body).getBody();
        }
        return body;
    }
}
