package com.backendsys.exception;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import com.backendsys.modules.dingtalk.utils.DingTalkUtil;
import com.backendsys.utils.response.ResultEnumService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.backendsys.utils.response.Result;
import com.backendsys.utils.response.ResultEnum;
import com.google.common.collect.Lists;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult;
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.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.security.auth.login.LoginException;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * https://juejin.cn/post/6844903902811275278
 * https://juejin.cn/post/7132747726965440520
 * 待看
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ResultEnumService resultEnumService;

    /**
     * 是否开启功能 true:开启
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    /**
     * 返回结果拦截器 (可以修改响应体的逻辑，或添加其他自定义逻辑)
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//        System.out.println("beforeBodyWrite:");
//        System.out.println(o);
//        if(o instanceof String){
//            return o;
//            try {
//                return objectMapper.writeValueAsString(Result.success(o));
//            } catch (JsonProcessingException e) {
//                e.printStackTrace();
//            }
//        }
//        //返回类型是否已经封装
//        if(o instanceof Result){
//            return Result.success(o);
//        }
        return o;
    }

    // -- 记录当前 访问URL、访问人IP --------------------------------------------------
    private static void printRequestInfo() {
        // 获取请求的URL
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            if (request != null) {
                String method = request.getMethod();
                String url = request.getRequestURL().toString();
                String ip = request.getRemoteAddr();

                // 记录URL和IP地址
                log.warn("[" + method + "] " + url + ", (" + ip + ")");
            }
        }
    }

    // -- 自定义异常输出结构 (警告) ----------------------------------------------------
    private static void printWarnException(Exception e) {
        printRequestInfo();
        log.warn(e.getClass().getName() + ": " + e.getMessage());
    }

    // -- 自定义异常输出结构 (错误) ----------------------------------------------------
    private static void printErrorException(Exception e, Boolean isPrintStack) {
        log.error("========================================================================");
        // printRequestInfo();

        // 获取请求的URL
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            if (request != null) {
                String url = request.getRequestURL().toString();
                String ip = request.getRemoteAddr();
                String method = request.getMethod();
                String createTime = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
                String serverName = request.getServerName();

                // 访问根目录报404也会触发此错误提示

//                // --- 向钉钉发送消息 -----------------------------------
//                // (本地环境不触发)
//                if (!"localhost".equals(serverName)) {
//                    // 创建一个 CompletableFuture 来执行异步任务
//                    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//                        try {
//                            String message = "【系统异常】：" + e.getMessage() + "\n" +
//                                    "【异常接口】：" + url + "\n" +
//                                    "【异常堆栈】：" + Convert.toStr(e.getStackTrace()).substring(0, 400) + "\n" +
//                                    "【异常时间】：" + createTime + "\n" +
//                                    "【异常来源】：" + ip;
//                            DingTalkUtil.sendTextMsg(message, Lists.newArrayList(), Lists.newLinkedList(), false);
//                        } catch (Exception ex) {
//                            System.out.println("钉钉群消息发送异常，异常原因：" + ex.getMessage());
//                            log.error(ex.getMessage());
//                        }
//                    });
//                }
//                // ---------------------------------------------------

                // 记录URL和IP地址
                log.error("[" + method + "] " + url + ", (" + ip + ")");
            }
        }

        // 记录 当前异常类的class名称、异常消息
        log.error(e.getMessage() + " (" + e.getClass().getName() + ")");

        // 记录异常的详细信息
        if (isPrintStack) {
//            log.error(Convert.toStr(e.getStackTrace()).substring(0, 400));
             e.printStackTrace();
        }
        log.error("========================================================================");
    }
    private static void printErrorException(Exception e) {
        printErrorException(e, false);
    }
    // -----------------------------------------------------------------------

    /**
     * 异常 参数错误
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleConstraintViolationException(ConstraintViolationException e) {

        System.out.println("****** ConstraintViolationException.class: ******");
        printErrorException(e);

        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
        List<String> msgList = new ArrayList<>();
        while (iterator.hasNext()) {
            ConstraintViolation<?> cvl = iterator.next();
            msgList.add(cvl.getMessageTemplate());
        }
        System.out.println(msgList.get(0));
        log.error(msgList.get(0));

        return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), msgList.get(0));
    }


    /**
     * 参数校验异常 (Validate)
     */
    // 未测试
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handlerMethodArgumentException(MethodArgumentNotValidException e){
        System.out.println("****** MethodArgumentNotValidException.class: ******");
        printWarnException(e);
        return Result.error(
            ResultEnum.PARAMETER_EXCEPTION.getCode(),
            e.getBindingResult().getAllErrors().get(0).getDefaultMessage(),
            "Field: " + e.getBindingResult().getFieldError().getField()
        );
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        System.out.println("****** MissingServletRequestParameterException.class: ******");
        printErrorException(e, false);
        return Result.error(
            ResultEnum.PARAMETER_EXCEPTION.getCode(),
            "缺少参数(" + e.getParameterName() + ")或参数类型不匹配(" + e.getParameterType() + ")",
            "Field: " + e.getParameterName()
        );
    }

    // 无效？
    @ExceptionHandler(IllegalStateException.class)
    public Result handleIllegalStateException(IllegalStateException e) {
        printErrorException(e, true);
        return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage());
    }
    @ExceptionHandler(BindingException.class)
    public Result handleBindingException(BindingException e) {
        printErrorException(e, true);
        return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage());
    }


    /**
     * 参数类型异常
     */
    @ExceptionHandler(NumberFormatException.class)
    public Result handleNumberFormatException(NumberFormatException e) {
        System.out.println("****** NumberFormatException.class: ******");
        printErrorException(e, true);
        return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage());
    }

    /**
     * 自定义异常类
     * throw new CustException("错误提示")
     */
    @ExceptionHandler(CustException.class)
    public Result handleCustException(CustException e) {
        System.out.println("****** CustException.class: ******");
        printWarnException(e);
//        printErrorException(e);
        return Result.error(e.getErrorCode() != null ? e.getErrorCode() : ResultEnum.PARAMETER_EXCEPTION.getCode(), e.getMessage(), e.getErrorObject());
    }
    /**
     * 自定义异常类 (仅返回文本)
     */
    @ExceptionHandler(CustExceptionSimple.class)
    public String handleCustExceptionSimple(CustExceptionSimple e) {
        System.out.println("****** CustExceptionSimple.class: ******");
        printErrorException(e);
        return e.getMessage();
    }


    /**
     * MyBatis异常
     */
    @ExceptionHandler(TooManyResultsException.class)
    public Result handleTooManyResultsException(TooManyResultsException e) {
        System.out.println("****** TooManyResultsException.class: ******");
        printErrorException(e, true);
        return Result.error(ResultEnum.SERVICE_EXCEPTION.getCode(), e.getMessage());
    }

    /**
     * 登录异常
     */
    @ExceptionHandler(LoginException.class)
    public Result handleLoginException(LoginException e) {
        System.out.println("****** LoginException.class: ******");
        printErrorException(e, true);
        return Result.error(ResultEnum.AUTH_ERROR.getCode(), e.getMessage());
    }



    
    /**
     * 权限异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public Result handleAccessDeniedException(AccessDeniedException e) {
        System.out.println("****** AccessDeniedException.class: ******");
        printWarnException(e);
//        return Result.error(ResultEnum.AUTH_ROLE_ERROR.getCode(), ResultEnum.AUTH_ROLE_ERROR.getMessage());
        return Result.error(ResultEnum.AUTH_ROLE_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.AUTH_ROLE_ERROR));
    }


    /**
     * 接口不存在 异常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Result handleNoHandlerFoundException(NoHandlerFoundException e) {
        System.out.println("****** NoHandlerFoundException.class: ******");
        printErrorException(e, false);
        return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage());
    }

    /**
     * Mybatis 异常抛出
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public Result handleMyBatisSystemException(MyBatisSystemException e, BindingResult bindingResult) { // , BindingResult bindingResult
        System.out.println("****** MyBatisSystemException.class: ******");
        printErrorException(e, true);

        Throwable cause = e.getCause();
        if (cause instanceof PersistenceException) {
            Throwable sqlException = cause.getCause();
            if (sqlException != null ) {
                if (sqlException.getMessage() == "argument type mismatch") {
                    System.out.println(sqlException.getMessage() + ", 参数类型不匹配");
                    log.error(sqlException.getMessage() + ", 参数类型不匹配");
                }
//                return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(), sqlException.getMessage());
                return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.INTERNAL_ERROR), sqlException.getMessage());
            } else {
                // [抛出异常] 实体类缺少字段
//                return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), ResultEnum.INTERNAL_ERROR.getMessage(), cause.getMessage());
                return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.INTERNAL_ERROR), cause.getMessage());
            }
        }
        return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage());
    }

    /**
     * MySQL 数据库 唯一值异常
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public Result handleDuplicateKeyException(DuplicateKeyException e) {
        System.out.println("****** DuplicateKeyException.class: ******");
        printErrorException(e, true);
        DuplicateKeyExceptionHandler handler = new DuplicateKeyExceptionHandler();
        return handler.handleDuplicateKeyException(e);
    }

    /**
     * Redis 连接异常
     */
    @ExceptionHandler(RedisConnectionFailureException.class)
    public Result handleRedisConnectionFailureException(Exception e) {
        System.out.println("****** RedisConnectionFailureException.class: ******");
        printErrorException(e, true);
        return Result.error(ResultEnum.REDIS_ERROR.getCode(), e.getMessage());
    }

    /**
     * 请求体参数不能为空 (body)
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        System.out.println("****** HttpMessageNotReadableException.class: ******");
        printWarnException(e);
        return Result.error(ResultEnum.HTTP_BODY_EMPTY.getCode(), e.getMessage() != null ? e.getMessage() : ResultEnum.HTTP_BODY_EMPTY.getMessage());
    }

    /**
     * 请求方法不支持 (Get/Post)
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        System.out.println("****** HttpRequestMethodNotSupportedException.class: ******");
        printWarnException(e);
//        return Result.error(ResultEnum.HTTP_METHOD_ERROR.getCode(), ResultEnum.HTTP_METHOD_ERROR.getMessage());
        return Result.error(ResultEnum.HTTP_METHOD_ERROR.getCode(), resultEnumService.getMessage(ResultEnum.HTTP_METHOD_ERROR));
    }

    /**
     * 其他异常:
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) { // , BindingResult bindingResult
        System.out.println("****** Exception.class: ******");
        printErrorException(e, true);
        return Result.error(ResultEnum.INTERNAL_ERROR.getCode(), e.getMessage());
    }

    // 非法参数
    @ExceptionHandler(IllegalArgumentException.class)
    public Result handleIllegalArgumentException(IllegalArgumentException e) {
        System.out.println("****** IllegalArgumentException.class: ******");
        printErrorException(e);
//        return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), ResultEnum.PARAMETER_EXCEPTION.getMessage(), e.getMessage());
        return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), resultEnumService.getMessage(ResultEnum.PARAMETER_EXCEPTION), e.getMessage());
    }

    // 优先级太高，会覆盖其他异常
//    @ExceptionHandler(RuntimeException.class)
//    public Result handleRuntimeException(RuntimeException e) {
//        System.out.println("****** RuntimeException.class: ******");
//        System.out.println(e);
////        System.out.println(e.getMessage());
////        if (e.getClass().getTypeName() == "java.lang.NullPointerException") {
////            return Result.error(ResultEnum.NULL_POINTER.getCode(), e.getMessage());
////        }
//        return Result.error(ResultEnum.SERVICE_EXCEPTION.getCode(), e.getMessage());
//    }

    @ExceptionHandler(WebClientResponseException.class)
    public Result handleWebClientResponseException(WebClientResponseException e) {
        System.out.println("****** WebClientResponseException.class: ******");
        printErrorException(e);
        return Result.error(ResultEnum.SERVICE_EXCEPTION.getCode(), resultEnumService.getMessage(ResultEnum.SERVICE_EXCEPTION), e.getMessage());
    }

}

