package com.nis.restful;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
@Component
public class DefaultRestErrorResolver implements RestErrorResolver,InitializingBean{
	
	/**
	 * 预先定义的所有异常类型
	 */
	private Map<String, String> exceptionMappings = Collections.emptyMap();
	
	/**
	 * 配置文件中自定义扩展错误
	 */
	private Map<String, String> exceptionMappingDefinitions = Collections.emptyMap();
	

	@Override
	public RestResult resolveError(HttpServletRequest request, Object handler,
			Exception ex) {
		RestResult error = this.buildError(ex, request);		
		return error;
	} 
	
	
	private RestResult buildError(Exception ex, HttpServletRequest request) {
		RestResult error = new RestResult();
		error.setStatus(this.getHttpStatusByEx(ex));// 设置http状态
		
		//RestServiceException 包含有错误code
		if(ex instanceof RestServiceException){
			//获取日志源[只有日志需要返回日志源和ActiveSys]
			int logSource = ((RestServiceException) ex).getLogSource();
			error.setTraceCode(((RestServiceException) ex).getTraceCode());
			int errorCode = ((RestServiceException) ex).getErrorCode();
			error.setBusinessCode(RestBusinessCode.valueOf(errorCode));
		} else if(ex instanceof ServiceRuntimeException){
			//获取日志源[只有日志需要返回日志源和ActiveSys]
			int logSource = ((ServiceRuntimeException) ex).getLogSource();
			error.setTraceCode(((ServiceRuntimeException) ex).getTraceCode());
			int errorCode = ((ServiceRuntimeException) ex).getErrorCode();
			error.setBusinessCode(RestBusinessCode.valueOf(errorCode));
		} else {
			error.setBusinessCode(RestBusinessCode.valueOf(998));
		}
		error.setMsg(ex.getMessage());
		if(null != request){
			error.setFromUri(request.getRequestURI());
		}else {
			error.setFromUri("unknow url");
		}
	
		return error;
	}
	
	private HttpStatus getHttpStatusByEx(Exception ex){
		if(this.exceptionMappingDefinitions.containsKey(ex.getClass().getName())){
			return HttpStatus.valueOf(Integer.parseInt(this.exceptionMappingDefinitions.get(ex.getClass()
					.getName())));
		} else {
			return HttpStatus.INTERNAL_SERVER_ERROR;
		}
	}
	

	@Override
	public void afterPropertiesSet() throws Exception {
		//初始化所有预先定义的错误
		this.exceptionMappings = createDefaultExceptionMappingDefinitions();
		//如果存在自定义的错误,添加到预定义错误中
		if (this.exceptionMappingDefinitions != null && !this.exceptionMappingDefinitions.isEmpty()) {
			this.exceptionMappings.putAll(this.exceptionMappingDefinitions);
        }
	}
	
	/**
	 * 将spring中的http状态和异常互相对应 参考 spring的defaultExceptionHandler实现类似
	 * 
	 * @return
	 */
	private final Map<String, String> createDefaultExceptionMappingDefinitions() {

		Map<String, String> m = new LinkedHashMap<String, String>();

		// 400
		applyDef(m, HttpMessageNotReadableException.class,
				HttpStatus.BAD_REQUEST);
		applyDef(m, MissingServletRequestParameterException.class,
				HttpStatus.BAD_REQUEST);
		applyDef(m, TypeMismatchException.class, HttpStatus.BAD_REQUEST);
		applyDef(m, "javax.validation.ValidationException",
				HttpStatus.BAD_REQUEST);

		// 404
		applyDef(m, NoSuchRequestHandlingMethodException.class,
				HttpStatus.NOT_FOUND);
		applyDef(m, "org.hibernate.ObjectNotFoundException",
				HttpStatus.NOT_FOUND);
		
		// 405
		applyDef(m, HttpRequestMethodNotSupportedException.class,
				HttpStatus.METHOD_NOT_ALLOWED);

		// 406
		applyDef(m, HttpMediaTypeNotAcceptableException.class,
				HttpStatus.NOT_ACCEPTABLE);

		// 409
		applyDef(m, "org.springframework.dao.DataIntegrityViolationException",
				HttpStatus.CONFLICT);

		// 415
		applyDef(m, HttpMediaTypeNotSupportedException.class,
				HttpStatus.UNSUPPORTED_MEDIA_TYPE);
		return m;
	}

	private void applyDef(Map<String, String> m, Class clazz, HttpStatus status) {
		applyDef(m, clazz.getName(), status);
	}

	private void applyDef(Map<String, String> m, String key, HttpStatus status) {
		m.put(key, status.value() + "");
	}


	public Map<String, String> getExceptionMappings() {
		return exceptionMappings;
	}


	public void setExceptionMappings(Map<String, String> exceptionMappings) {
		this.exceptionMappings = exceptionMappings;
	}


	public Map<String, String> getExceptionMappingDefinitions() {
		return exceptionMappingDefinitions;
	}


	public void setExceptionMappingDefinitions(
			Map<String, String> exceptionMappingDefinitions) {
		this.exceptionMappingDefinitions = exceptionMappingDefinitions;
	}
	
	
	
	
	
}
