package com.czg.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.hutool.core.exceptions.ValidateException;
import com.czg.resp.CzgRespCode;
import com.czg.resp.CzgResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GYJoker
 */
@RestControllerAdvice
@Slf4j
public class CzgControllerAdvice {
    @ResponseBody
    @ExceptionHandler(value = NoResourceFoundException.class)
    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> errorHandler(NoResourceFoundException ex) {
        log.error("资源不存在");
        return CzgResult.failure(CzgRespCode.SYSTEM_ERROR.getCode(), ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = NotLoginException.class)
    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> notLoginErrorHandler(NotLoginException ex) {
        return CzgResult.failure(CzgRespCode.NOT_LOGIN.getCode(), CzgRespCode.NOT_LOGIN + ":" + ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> invalidFormatExceptionErrorHandler(HttpMessageNotReadableException ex) {
        setErrorLog(ex);
        return CzgResult.failure(ex.getRootCause() == null ? ex.getMessage() : ex.getRootCause().getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = NotPermissionException.class)
    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> notPermissionErrorHandler() {
        return CzgResult.failure(CzgRespCode.UN_PERMISSION);
    }

    @ResponseBody
    @ExceptionHandler(value = NotRoleException.class)
    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> notRoleErrorHandler() {
        return CzgResult.failure(CzgRespCode.UN_ROLE);
    }

    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> requestParamException(MethodArgumentNotValidException ex) {
//        setErrorLog(ex);
        log.error("传参加校验失败:{}", ex.getMessage());
        List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
        String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
        return CzgResult.failure(CzgRespCode.PARAM_ERROR.getCode(), message);
    }

    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public CzgResult<Object> methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        log.error("参数类型转换异常:{}", ex.getMessage());
        return CzgResult.failure(CzgRespCode.PARAM_ERROR.getCode(), ex.getMessage());
    }


    /**
     * 处理自定义异常
     */
    @ExceptionHandler(CzgException.class)
    public CzgResult<Object> handleCzgException(CzgException ex) {
//        setErrorLog(ex);
        log.error("CzgException校验异常:{}", ex.getMessage());
        return CzgResult.failure(ex.getCode(), ex.getMsg());
    }

    //    @ResponseBody
    @ExceptionHandler(OrderValidateException.class)
//    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> handleOrderValidateException(OrderValidateException ex) {
        log.error("订单校验异常:{}", ex.getMessage());
        return CzgResult.failure(ex.getCode(), ex.getMsg());
    }

    /**
     * 支付成功
     * 支付金额为零的统一处理
     */
    @ExceptionHandler(PaySuccessException.class)
    public CzgResult<Object> handlePaySuccessException() {
        return CzgResult.success("支付成功");
    }

    /**
     * 订单超时 取消
     */
    @ExceptionHandler(OrderCancelException.class)
    public CzgResult<Object> handleOrderCancelException() {
        return CzgResult.failure(701, "订单已过期,请重新下单");
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public CzgResult<Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
        log.error("传参错误:{}", ex.getMessage());
        return CzgResult.failure(CzgRespCode.SYSTEM_ERROR.getCode(), "系统异常，请确认参数");
    }

    /**
     * 处理Hutool的断言抛出异常
     */
//    @ExceptionHandler(IllegalArgumentException.class)
//    public CzgResult<Object> handleAssertException(IllegalArgumentException ex) {
//        return CzgResult.failure(CzgRespCode.PARAM_ERROR.getCode(), ex.getMessage());
//    }


    /**
     * get/post 等方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public CzgResult<Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        return CzgResult.failure(CzgRespCode.UN_PERMISSION.getCode(), ex.getMessage());
    }

    /**
     * 处理Hutool的校验工具类抛出异常
     */
    @ExceptionHandler(ValidateException.class)
    public CzgResult<Object> handleValidateException(ValidateException ex) {
        log.error(ex.getMessage());
        return CzgResult.failure(CzgRespCode.PARAM_ERROR.getCode(), ex.getMessage());
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public CzgResult<Object> handleDuplicateKeyException(DuplicateKeyException ex) {
        setErrorLog(ex);
        return CzgResult.failure(CzgRespCode.RECORD_EXISTED);
    }

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public CzgResult<Object> errorHandler(Exception ex) {
        log.error("系统未处理异常", ex);
        String rootMsg = NestedExceptionUtils.getMostSpecificCause(ex).getMessage();
        return CzgResult.failure(CzgRespCode.FAILURE.getCode(), rootMsg);

//        Throwable rootCause = ex;
//        while (rootCause.getCause() != null && !(rootCause instanceof CzgException)) {
//            rootCause = rootCause.getCause();
//        }
//
//        // 2. 如果根因是CzgException，直接抛出/返回该异常
//        if (rootCause instanceof CzgException exception) {
//            return CzgResult.failure(exception.getCode(), exception.getMsg());
//        }else if (rootCause instanceof ValidateException exception) {
//            return CzgResult.failure(exception.getStatus(), exception.getMessage());
//        }

        // 3. 非业务异常，按原有逻辑处理
//        return CzgResult.failure(CzgRespCode.SYSTEM_ERROR.getCode(), "系统错误，请联系管理员");
    }

    private void setErrorLog(Exception ex) {
        log.error(ex.getMessage());
        log.error("错误", ex);
    }
}
