package com.bcxin.rest.web.commons;

import com.bcxin.Infrastructures.ExceptionConstant;
import com.bcxin.Infrastructures.components.JsonProvider;
import com.bcxin.Infrastructures.exceptions.*;
import com.bcxin.Infrastructures.utils.ExceptionUtil;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
    private final WebLoggerProvider webLoggerProvider;
    private final JsonProvider jsonProvider;

    public RestResponseEntityExceptionHandler(WebLoggerProvider webLoggerProvider,
                                              JsonProvider jsonProvider) {
        this.webLoggerProvider = webLoggerProvider;
        this.jsonProvider = jsonProvider;
    }

    @ExceptionHandler
    public ResponseEntity exceptionHandle(Exception ex, WebRequest request) {
        boolean recordLog = false;
        try {
            MethodArgumentNotValidException invalidException = ExceptionUtil.cast(ex, MethodArgumentNotValidException.class);
            if (invalidException != null) {
                Collection<String> errors = invalidException.getBindingResult()
                        .getAllErrors().stream()
                        .map(ii -> {
                            StringBuilder msg = new StringBuilder();
                            Object[] arguments = ii.getArguments();
                            if (arguments != null && arguments.length > 0) {
                                String fieldName = Arrays.stream(arguments).map(arg -> {
                                    DefaultMessageSourceResolvable dmsr = (DefaultMessageSourceResolvable) arg;
                                    if (dmsr != null) {
                                        return dmsr.getDefaultMessage();
                                    }

                                    return null;
                                }).filter(arg -> arg != null).collect(Collectors.joining(","));

                                msg.append(fieldName);
                                msg.append(" ");
                            }

                            msg.append(ii.getDefaultMessage());

                            return msg.toString();
                        }).collect(Collectors.toList());

                recordLog = true;
                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, errors);
            }

            DataOperationTenantException dtx = ExceptionUtil.cast(ex, DataOperationTenantException.class);
            if (dtx != null) {
                return ResponseBuilder.build(HttpStatus.METHOD_NOT_ALLOWED, dtx.getData(), dtx.getCode(), dtx.getMessage(), null);
            }

            UnAuthorizedTenantException authorizedException = ExceptionUtil.cast(ex, UnAuthorizedTenantException.class);
            if (authorizedException != null) {
                return ResponseBuilder.build(HttpStatus.UNAUTHORIZED, null, ex.getMessage(), null);
            }

            ForbidTenantException forbidException = ExceptionUtil.cast(ex, ForbidTenantException.class);
            if (forbidException != null) {
                return ResponseBuilder.build(HttpStatus.FORBIDDEN, null, forbidException.getMessage(), null);
            }

            NotFoundTenantException notFoundException = ExceptionUtil.cast(ex, NotFoundTenantException.class);
            if (notFoundException != null) {
                return ResponseBuilder.build(HttpStatus.NOT_FOUND, null, notFoundException.getMessage(), null);
            }

            NotAllowedTenantException notAllowedException = ExceptionUtil.cast(ex, NotAllowedTenantException.class);
            if (notAllowedException != null) {
                return ResponseBuilder.build(HttpStatus.METHOD_NOT_ALLOWED, null, notAllowedException.getMessage(), null);
            }

            AccountLockedException accountLockedException = ExceptionUtil.cast(ex, AccountLockedException.class);
            if (accountLockedException != null) {
                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, null, ExceptionConstant.LOGIN_LOCK_EXCEPTION_CODE, accountLockedException.getMessage(), null);
            }

            AbnormalPersonnelException abnormalPersonnelException = ExceptionUtil.cast(ex, AbnormalPersonnelException.class);
            if (abnormalPersonnelException != null) {
                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, abnormalPersonnelException.getExceptionList(), abnormalPersonnelException.getCode(), abnormalPersonnelException.getMessage(), null);
            }

            ConflictTenantException conflictTenantException = ExceptionUtil.cast(ex, ConflictTenantException.class);
            if (conflictTenantException != null) {
                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, null, conflictTenantException.getMessage(), null);
            }


            TenantExceptionAbstract tenantException = ExceptionUtil.cast(ex, TenantExceptionAbstract.class);
            if (tenantException != null) {
                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, null, tenantException.getMessage(), null);
            }

            String detailException = ExceptionUtil.getStackMessage(ex);
            if (detailException.contains("IdentityNotFoundException") || detailException.contains("账号/密码不正确!")) {
                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, detailException, "账号/密码不正确!", null);
            }
            if (detailException.contains("IdentityNotMatchedException") || detailException.contains("账号/密码不正确")) {
                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, detailException, "账号/密码不正确!", null);
            }

            recordLog = true;
            if (detailException.contains("TenantException:") && detailException.contains("\n")) {
                int index = detailException.indexOf("TenantException:") + 16;
                int lastIndex = detailException.indexOf("\r");
                if (detailException.length() > index && lastIndex > index) {
                    String msg = detailException.substring(index, lastIndex);
                    return ResponseBuilder.build(HttpStatus.BAD_REQUEST, null, msg, null);
                } else {
                    return ResponseBuilder.build(HttpStatus.BAD_REQUEST, null, detailException, null);
                }
            }

            return ResponseBuilder.build(HttpStatus.BAD_REQUEST, detailException, "系统异常, 请联系管理员", null);
        } finally {
            if (recordLog) {
                String detail = ExceptionUtil.getStackMessage(ex);
                logError("RestResponseEntityExceptionHandler.exceptionHandle", detail, request);
            }
        }
    }

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        ValidationErrorResponse validationErrorResponse = new ValidationErrorResponse("请求参数不符合要求");
        for (ObjectError error : ex.getBindingResult().getAllErrors()) {
            if (error instanceof FieldError) {
                FieldError fieldError = (FieldError) error;
                validationErrorResponse.addFieldMessage(fieldError.getField(), fieldError.getDefaultMessage(), fieldError.getRejectedValue());
            } else {
                validationErrorResponse.addFieldMessage(error.getObjectName(), error.getDefaultMessage());
            }
        }

        return ResponseBuilder.build(HttpStatus.BAD_REQUEST, validationErrorResponse);
    }

    private void logError(String title, String detail, WebRequest request) {
        try {
            webLoggerProvider.error(title, detail, request);
        } catch (Exception ex) {

        }
    }

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        ResponseEntity<Object> responseEntity = super.handleExceptionInternal(ex, body, headers, status, request);
        if (status == HttpStatus.BAD_REQUEST || status == HttpStatus.INTERNAL_SERVER_ERROR) {
            String detail = String.format("body=%s;exception=%s",
                    this.jsonProvider.getJson(body),
                    ExceptionUtil.getStackMessage(ex)
            );
            try {
                String friendMessage = "系统异常, 请联系管理员";
                if (ex instanceof HttpMessageConversionException) {
                    friendMessage = "参数格式不正确, 请联系管理员进行确认(02)";
                }

                return ResponseBuilder.build(HttpStatus.BAD_REQUEST, detail, friendMessage, null);
            } finally {
                logError("RestResponseEntityExceptionHandler.handleExceptionInternal", detail, request);
            }
        }

        return responseEntity;
    }
}
