package com.ytpm.question.handle;

import cn.hutool.core.util.StrUtil;
import com.ytpm.general.Result;
import com.ytpm.general.StatusCode;
import com.ytpm.handle.CommonException;
import com.ytpm.handle.CustomerException;
import com.ytpm.handle.ValidatedException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Set;

/**
 * @author Marx
 * @date 2025/8/28 10:38
 */
@Slf4j(topic = "answer-exception")
@RestControllerAdvice(annotations = RestController.class)
public class CustomerExceptionHandler implements HandlerExceptionResolver {

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception ex) {
        //打印异常信息
        log.error(StrUtil.format("抛出异常信息，异常信息为：ex={}", ex.getMessage()), ex);
        String msg = "查询超时，请稍后重试！";
        return new Result<>(StatusCode.ERROR, msg);
    }

    /**
     * 对象参数校验失败
     *
     */
    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<?> handleMissingServletRequestParameterException(Exception ex) {
        log.error(StrUtil.format("对象参数校验失败，异常信息为：e={}", ex.getMessage()), ex);
        String msg = MessageFormat.format("缺少参数{0}", ((MissingServletRequestParameterException) ex).getParameterName());
        return new Result<>(StatusCode.MISSING_PARAMETER, msg);
    }

    /**
     * 单个参数校验失败
     */
    @ResponseBody
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<?> handleConstraintViolationException(Exception ex) {
        log.error(StrUtil.format("单个参数校验失败，异常信息为：ex={}", ex.getMessage()), ex);
        String returnMessage = "参数校验失败";
        Set<ConstraintViolation<?>> sets = ((ConstraintViolationException) ex).getConstraintViolations();
        if (!CollectionUtils.isEmpty(sets)) {
            StringBuilder sb = new StringBuilder();
            sets.forEach(error -> sb.append(error.getMessage()).append(";"));
            String msg = sb.toString();
            returnMessage = StrUtil.sub(msg, 0, msg.length() - 1);
        }
        return new Result<>(StatusCode.PARAMETER_CHECK_ERR, returnMessage);
    }

    /**
     * 自定义校验异常信息捕获
     *
     */
    @ResponseBody
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<?> handleSpringCheckException(Exception ex) {
        log.error(StrUtil.format("自定义校验[IllegalArgumentException]异常信息捕获，异常信息为：ex={}", ex.getMessage()), ex);
        return new Result<>(StatusCode.PARAMETER_CHECK_ERR, ex.getMessage());
    }

    /**
     * POST 请求参数校验失败
     *
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleMethodArgumentNotValidException(Exception ex) {
        log.error(StrUtil.format("请求参数校验失败，异常信息为：ex={}", ex.getMessage()), ex);
        List<ObjectError> errors = ((MethodArgumentNotValidException) ex).getBindingResult().getAllErrors();
        String msg = "绑定异常";
        if (!CollectionUtils.isEmpty(errors)) {
            StringBuilder sb = new StringBuilder();
            errors.forEach(error -> sb.append(error.getDefaultMessage()).append(";"));
            msg = StrUtil.sub(sb.toString(), 0, sb.length() - 1);
        }
        return new Result<>(StatusCode.PARAMETER_CHECK_ERR, msg);
    }

    @ResponseBody
    @ExceptionHandler(ValidatedException.class)
    public Result<?> handleValidatedException(Exception ex) {
        log.error(" 请求参数校验失败，异常信息为：ex={}", ex.getMessage());
        return new Result<>(StatusCode.BUSINESS_CERTIFICATE_TYPE_STATUS_ERROR, ex.getMessage());
    }

    /**
     * 上传文件超出限制处理
     */
    @ResponseBody
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public Result<?> handlerFileUploadException(MaxUploadSizeExceededException ex) {
        log.error(StrUtil.format("抛出异常信息，异常信息为：ex={}", ex.getMessage()), ex);
        return new Result<>(StatusCode.ERROR, "文件超出限制(10MB)");
    }

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                         Object o, Exception e) {
        log.error(StrUtil.format("系统异常，异常信息为：e={}", e.getMessage()), e.getMessage());
        ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
        if (e instanceof MultipartException) {
            mv.addObject("message", "系统异常");
            mv.addObject("code", "801");
            mv.setStatus(HttpStatus.OK);
        } else {
            mv.addObject("msg", e.getMessage());
        }
        return mv;
    }

    @ResponseBody
    @ExceptionHandler(CustomerException.class)
    public Result<?> handleCustomerException(Exception ex) {
        log.error(StrUtil.format("自定义校验异常[CustomerException]信息捕获，异常信息为：ex={}", ex), ex);
        return new Result<>(StatusCode.ACCESS_ERR, ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(CommonException.class)
    public Result<?> handleCommonException(Exception ex) {
        log.error(StrUtil.format("自定义校验异常[CommonException]信息捕获，异常信息为：ex={}", ex), ex);
        return new Result<>(StatusCode.ACCESS_ERR, ex.getMessage());
    }


}
