package com.yonge.cooleshow.mall.common.exception;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yonge.cooleshow.mall.common.api.CommonResult;
import com.yonge.toolset.base.exception.BizException;
import com.yonge.toolset.utils.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 全局异常处理
 * Created by macro on 2020/2/27.
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = ApiException.class)
    public CommonResult handle(ApiException e) {
        if (e.getErrorCode() != null) {
            return CommonResult.failed(e.getErrorCode());
        }
        return CommonResult.failed(e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult handleValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        String message = null;
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                message = fieldError.getField()+fieldError.getDefaultMessage();
            }
        }

        // 发送机器人消息
        sendDingTalkRobotNotify(e);

        return CommonResult.validateFailed(message);
    }

    @ResponseBody
    @ExceptionHandler(value = BindException.class)
    public CommonResult handleValidException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        String message = null;
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                message = fieldError.getField()+fieldError.getDefaultMessage();
            }
        }

        // 发送机器人消息
        sendDingTalkRobotNotify(e);

        return CommonResult.validateFailed(message);
    }


    @ResponseBody
    @ExceptionHandler(value = BizException.class)
    public CommonResult handleBizException(BizException e) {
        return  CommonResult.failed(e.getMessage());
    }



    /**
     * 获取异常信息
     * @param ex Throwable
     * @return Throwable
     */
    private static Throwable getThrowable(Throwable ex) {
        return Optional.ofNullable(ExceptionUtils.getRootCause(ex)).orElse(ex);
    }

    /**
     * 机器人发送消息
     * @param e Throwable
     */
    private void sendDingTalkRobotNotify(Throwable e) {
        try {

            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (Objects.nonNull(attributes)) {

                // 机器人通知消息
                String robotMessage = getRobotRequestLog(attributes.getRequest());

                Map<String, Object> paramMap = new HashMap<>(2);
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("content", "系统繁忙请及时处理: " + robotMessage + "   " + getThrowable(e));
                paramMap.put("text", jsonObject.toJSONString());
                paramMap.put("msgtype", "text");
                Map<String, String> headers = new HashMap<>(1);
                headers.put("Content-Type", "application/json");
                HttpUtil.postForHttps(dingTalkRobotsSecurityParam(), JSON.toJSONString(paramMap), headers);

            }
        } catch (Exception exception) {
            log.error("sendDingTalkRobotNotify Error", exception);
        }
    }

    /**
     * 打印异常请求日志
     * @param request HttpServletRequest
     * @return String
     */
    private String getRobotRequestLog(HttpServletRequest request){

        if (Objects.isNull(request)) {
            log.warn("getRobotRequestLog request is null");
            return "";
        }

        StringBuilder sb = new StringBuilder();
        // 打印请求日志或执行其他逻辑
        sb.append("Request URI: ").append(request.getRequestURI());

        // 请求参数
        Map<String, String> requestParams = getRequestParameters(request);
        if (!requestParams.isEmpty()) {
            sb.append(", Request Parameters: ").append(requestParams);
        }

        // 请求头
        Map<String, String> requestHeaders = getRequestHeaders(request);
        if (!requestHeaders.isEmpty()) {
            sb.append(", Request Headers: ").append(requestHeaders);
        }

        // 请求消息体
        String requestBody = getRequestBody(request);
        if (!requestBody.isEmpty()) {
            sb.append(", Request JSON: ").append(requestBody);
        }
        return sb.toString();
    }

    /**
     * 请求参数
     * @param request HttpServletRequest
     * @return Map<String, String>
     */
    private Map<String, String> getRequestParameters(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        request.getParameterMap().forEach((name, values) -> {
            if (values.length > 0) {
                params.put(name, values[0]);
            }
        });
        return params;
    }

    /**
     * 请求头
     * @param request HttpServletRequest
     * @return Map<String, String>
     */
    private Map<String, String> getRequestHeaders(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> headers = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headers.put(headerName, headerValue);
        }
        return headers;
    }

    /**
     * 请求消息体
     * @param request HttpServletRequest
     * @return String
     */
    private String getRequestBody(HttpServletRequest request) {
        StringBuilder requestBody = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
        } catch (IOException e) {
            log.error("robot getRequestBody body: {}", e.getMessage());
        }
        return requestBody.toString();
    }

    public String dingTalkRobotsSecurityParam() throws Exception {
        Long timestamp = System.currentTimeMillis();
        String secret = "SEC7371152e58a066b358afd9e9cfaad2aee4b46471218f1aeef88d43a401d8e2f2";
        String stringToSign = timestamp + "\n" + secret;
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
        StringBuffer sb = new StringBuffer("https://oapi.dingtalk.com/robot/send?access_token=324dc48b8af4fc21223a5b8fd5d8daa611e106f2711d91abe1d73bce8c5c86b2&timestamp=");
        sb.append(timestamp).append("&sign=").append(sign);
        return sb.toString();
    }
}
