package com.ximai.framework.interceptor;

import com.ximai.common.core.domain.AjaxResult;
import com.ximai.common.exception.ServiceException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ximai.common.utils.MessageUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
import java.util.Map;

@Component
public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver {
    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalHandlerExceptionResolver.class);
    private final JsonView view;

    public GlobalHandlerExceptionResolver(JsonView view) {
        this.view = view;
    }

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView = new ModelAndView(getView());
        modelAndView.addObject(AjaxResult.error(getOutputException(ex).getMessage()));
        response.setContentType("application/json");
        return modelAndView;
    }

    private View getView() {
        return view;
    }

    private Exception getOutputException(Exception ex) {
        LOGGER.error("异常", ex);
        if (ex instanceof ServiceException) {
            return ex;
        }

        if (ex instanceof DataAccessException) {
            SQLException sqlException = deepFindSQLException((DataAccessException) ex);
            if (sqlException != null) {
                switch (sqlException.getErrorCode()) {
                    case 1062:
                        return new ServiceException(MessageUtils.message("basic.error.repeat.add"), ex);
                    case 1451:
                        return new ServiceException(MessageUtils.message("basic.error.canNot.delete"), ex);
                    case 2627:
                        return new ServiceException(MessageUtils.message("basic.error.repeat.add"), ex);
                    case 547:
                        return new ServiceException(MessageUtils.message("basic.error.canNot.delete"), ex);
                    default:
                        break;
                }
            }
        }
        return ex;
    }

    private SQLException deepFindSQLException(DataAccessException ex) {
        int varDeep = 3;
        Throwable current = ex.getCause();
        while (varDeep > 0) {
            if (current == null) {
                return null;
            }
            if (current instanceof SQLException) {
                return (SQLException) current;
            }
            current = current.getCause();
            varDeep--;
        }
        return null;
    }

    @Component
    public static class JsonView implements View {

        private final ObjectMapper mapper;

        public JsonView(ObjectMapper mapper) {
            this.mapper = mapper;
        }

        @Override
        public String getContentType() {
            return "application/json;charset=utf-8";
        }

        @Override
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.getOutputStream().write(mapper.writeValueAsBytes(model.values().stream().findFirst()));
        }
    }
}

