package com.ibeetl.admin.core.conf;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ibeetl.admin.core.util.FormFieldException;
import com.ibeetl.admin.core.util.PlatformException;
import com.ibeetl.admin.core.web.JsonResult;

/**
 * 自定义的全局错误页面
 *
 * @author lijiazhi
 */
@Controller
public class CustomErrorController extends AbstractErrorController {

  private static final String ERROR_PATH = "/error";
  Log log = LogFactory.getLog(ErrorController.class);

  @Autowired ObjectMapper objectMapper;

  public CustomErrorController() {
    super(new DefaultErrorAttributes());
  }

  @RequestMapping(ERROR_PATH)
  public ModelAndView getErrorPath(HttpServletRequest request, HttpServletResponse response) {
    Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(request, false));
    Throwable cause = getCause(request);
    int status = (Integer) model.get("status");
    // 错误信息
    String message = (String) model.get("message");
    // 友好提示
    String errorMessage = getErrorMessage(cause);
    String requestPath = (String) model.get("path");

    List<FieldError> filedErrors = this.getFieldError(model, cause);

    // 后台打印日志信息方方便查错
    log.error(status + ":" + message + filedErrors, cause);
    log.error("requestPath" + ":" + requestPath);

    response.setStatus(status);
    if (!isJsonRequest(request)) {
      ModelAndView view = new ModelAndView("/error.html");
      view.addAllObjects(model);
      view.addObject("errorMessage", errorMessage);
      view.addObject("filedErrors", filedErrors);
      view.addObject("cause", cause);
      view.addObject("requestPath", requestPath);

      return view;

    } else {
      if (filedErrors == null) {
        if (status == 404) {
          writeJson(response, JsonResult.http404(requestPath));
        } else {
          writeJson(response, JsonResult.failMessage(getErrorMessage(cause)));
        }

      } else {

        writeJson(response, JsonResult.fail(this.wrapFieldErrors(filedErrors)));
      }

      return null;
    }
  }

  protected List<FieldError> getFieldError(Map<String, Object> model, Throwable cause) {
    List<FieldError> filedErrors = (List<FieldError>) model.get("errors");
    if (filedErrors != null) {
      return filedErrors;
    }
    if (cause instanceof FormFieldException) {
      FormFieldException fe = (FormFieldException) cause;
      return fe.getErrors();
    }
    return null;
  }

  protected List<Map<String, String>> wrapFieldErrors(List<FieldError> errors) {
    List<Map<String, String>> list = new ArrayList<Map<String, String>>();
    for (FieldError e : errors) {
      Map<String, String> error = new HashMap<String, String>();
      error.put("field", e.getField());
      error.put("message", e.getDefaultMessage());
      list.add(error);
    }
    return list;
  }

  protected boolean isJsonRequest(HttpServletRequest request) {
    String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");
    if (requestUri != null && requestUri.endsWith(".json")) {
      return true;
    } else {
      return (request.getHeader("Accept").contains("application/json")
          || (request.getHeader("X-Requested-With") != null
              && request.getHeader("X-Requested-With").contains("XMLHttpRequest")));
    }
  }

  protected void writeJson(HttpServletResponse response, JsonResult error) {
    response.setContentType("application/json;charset=utf-8");
    try {
      response.getWriter().write(objectMapper.writeValueAsString(error));
    } catch (IOException e) {
      // ignore
    }
  }

  protected String getErrorMessage(Throwable ex) {
    if (ex instanceof PlatformException) {
      return ex.getMessage();
    } else {
      return "服务器错误,请联系管理员";
    }
  }

  protected Throwable getCause(HttpServletRequest request) {
    Throwable error = (Throwable) request.getAttribute("javax.servlet.error.exception");
    if (error != null) {
      while (error instanceof ServletException && error.getCause() != null) {
        error = ((ServletException) error).getCause();
      }
    }
    return error;
  }

  @Override
  public String getErrorPath() {
    // TODO Auto-generated method stub
    return null;
  }
}
