package cn.meowrain.aioj.backend.framework.core.exception.handler;

import cn.meowrain.aioj.backend.framework.core.exception.AbstractException;
import cn.meowrain.aioj.backend.framework.core.web.Result;
import cn.meowrain.aioj.backend.framework.core.web.Results;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 全局错误捕获器
 */
@Slf4j
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {
	// 加这个构造器，启动看日志
	public GlobalExceptionHandler() {
		System.out.println("===== 自定义异常处理器已加载 =====");
	}

	/**
	 * 捕获所有参数错误，然后统一捕获并且抛出
	 * 
	 * @param request {@link HttpServletRequest}
	 * @param ex      {@link org.springframework.validation.method.MethodValidationException}
	 * @return {@link Result<Void>}
	 */
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public Result<Void> validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {
		BindingResult bindingResult = ex.getBindingResult();
		// 收集所有错误字段
		List<String> errorMessages = bindingResult.getFieldErrors()
				.stream()
				.map(FieldError::getDefaultMessage)
				.toList();
		String exceptionMessage = String.join(",", errorMessages);
		log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionMessage);
		return Results.paramsValidFailure();
	}

	/**
	 * 抽象异常捕获其
	 * 
	 * @param request {@link HttpServletRequest}
	 * @param ex      {@link AbstractException}
	 * @return {@link Result<Void>}
	 */
	@ExceptionHandler(value = { AbstractException.class })
	public Result<Void> abstractExceptionHandler(HttpServletRequest request, AbstractException ex) {
		if (ex.getCause() != null) {
			log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex, ex.getCause());
			return Results.failure(ex);
		}
		StringBuilder stackTraceBuilder = new StringBuilder();
		stackTraceBuilder.append(ex.getClass().getName()).append(": ").append(ex.getErrorMessage()).append("\n");
		StackTraceElement[] stackTrace = ex.getStackTrace();
		for (int i = 0; i < Math.min(5, stackTrace.length); i++) {
			stackTraceBuilder.append("\tat ").append(stackTrace[i]).append("\n");
		}
		log.error("[{}] {} [ex] {} \n\n{}", request.getMethod(), request.getRequestURL().toString(), ex,
				stackTraceBuilder);
		return Results.failure(ex);
	}

	/**
	 * 拦截未捕获异常
	 */
	@ExceptionHandler(value = Throwable.class)
	public Result<Void> defaultErrorHandler(HttpServletRequest request, Throwable throwable) {
		log.error("[{}] {} ", request.getMethod(), getUrl(request), throwable);
		return Results.failure();
	}

	/**
	 * 获取请求URL
	 * 
	 * @param request {@link HttpServletRequest}
	 * @return String
	 */
	private String getUrl(HttpServletRequest request) {
		if (!StringUtils.hasText(request.getQueryString())) {
			return request.getRequestURI();
		}
		return request.getRequestURL() + "?" + request.getQueryString();
	}

}
