package com.ejie.u74a.control;

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Locale;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
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.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;

import com.ejie.u74a.exception.AjaxTimeoutException;
import com.ejie.u74a.util.StackTraceManager;
import com.ejie.u74a.util.exception.NegocioException;
import com.ejie.u74a.util.exception.ParameterValidationException;

/**
 * Clase que se encarga de capturar las excepciones de la aplicación.
 * 
 *  
 */
@ControllerAdvice()
public class GlobalExceptionHandler {

	private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

	@Autowired()
	@Qualifier(value = "messageSource")
	private ReloadableResourceBundleMessageSource messageSource;

	/**
	 * Captura las excepciones SQL.
	 * 
	 * @param request Request
	 * @param response Response
	 * @param ex Excepción
	 * 
	 * @return vista
	 * 
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	@ExceptionHandler(value = { SQLException.class, DataAccessException.class })
	public String handleSQLException(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws IOException {
		logger.error("SQLException Occured:: URL= {}", request.getRequestURL());
		logger.error(StackTraceManager.getStackTrace(ex));

		String ajaxHeader = request.getHeader("X-Requested-With");

		if ("XMLHttpRequest".equals(ajaxHeader)) {
			logger.info("Llamada Ajax detectada");

			String mensajeError = this.messageSource.getMessage("error.baseDatos", null,
					LocaleContextHolder.getLocale());
			// response.setContentType("text/html;charset=UTF-8");
			// response.setCharacterEncoding("UTF-8");
			// response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, mensajeError);

			// Ñapa Weblogic para evitar error: ProtocolException: Didn't meet stated Content-Length
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			response.setContentType("text/html;charset=UTF-8");
			response.getWriter().write(mensajeError);
			response.flushBuffer();
			return null;
			// /////

		}
		return "database_error";
	}

	/**
	 * Captura las excepciones de entrada-salida
	 */
	@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "IOException occured")
	@ExceptionHandler(value = IOException.class)
	public void handleIOException() {
		logger.error("IOException handler executed");
		// returning 404 error code
	}

	/**
	 * Captura todas las excepciones y las redirige a la página de error.
	 * 
	 * @param request Request
	 * @param response Response
	 * @param ex Excepción
	 * 
	 * @return Modelo y vista
	 * 
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	@ExceptionHandler(value = Exception.class)
	public ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws IOException {
		logger.error("Requested URL = {}", request.getRequestURL());
		logger.error("Exception Raised = {}", ex);
		logger.error(StackTraceManager.getStackTrace(ex));

		String ajaxHeader = request.getHeader("X-Requested-With");

		if ("XMLHttpRequest".equals(ajaxHeader)) {
			logger.info("Llamada Ajax detectada");

			String mensajeError = this.messageSource.getMessage("error.desconocido", null,
					LocaleContextHolder.getLocale());
			// response.setContentType("text/html;charset=UTF-8");
			// response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, mensajeError);

			// Ñapa Weblogic para evitar error: ProtocolException: Didn't meet stated Content-Length
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			response.setContentType("text/html;charset=UTF-8");
			response.getWriter().write(mensajeError);
			response.flushBuffer();
			return null;
			// /////

		}

		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("exception", ex);
		modelAndView.addObject("url", request.getRequestURL());

		modelAndView.setViewName("error");
		return modelAndView;
	}

	/**
	 * Captura el timeout en una petición Ajax
	 * 
	 * @param request Request
	 * @param response Response
	 * 
	 * @throws IOException IOException
	 * 
	 */
	@ExceptionHandler(value = AjaxTimeoutException.class)
	// @ResponseStatus(value = HttpStatus.UNAUTHORIZED, reason = "La sesión ha expirado.")
	public void handleAjaxTimeout(HttpServletRequest request, HttpServletResponse response) throws IOException {
		logger.debug("handleAjaxTimeout");

		logger.info("Captura de timeout en petición Ajax");

		String mensajeError = this.messageSource.getMessage("error.sesionExpirada", null,
				LocaleContextHolder.getLocale());
		// response.setContentType("text/html;charset=UTF-8");
		// response.sendError(HttpServletResponse.SC_UNAUTHORIZED, mensajeError);

		// Ñapa Weblogic para evitar error: ProtocolException: Didn't meet stated Content-Length
		response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		response.setContentType("text/html;charset=UTF-8");
		response.getWriter().write(mensajeError);
		response.flushBuffer();
		// /////
	}

	/**
	 * Captura error de validación de parámetros en una petición Ajax.
	 * 
	 * @param request Request
	 * @param response Response
	 * @param excepcion Excepción
	 * 
	 * @throws IOException IOException
	 */
	@ExceptionHandler(value = ParameterValidationException.class)
	public void handleParameterValidationException(HttpServletRequest request, HttpServletResponse response,
			ParameterValidationException excepcion) throws IOException {
		logger.error("Exception Raised = {}", excepcion.getMessage());

		// response.sendError(HttpServletResponse.SC_BAD_REQUEST, excepcion.getMessage() + "  ");

		// Ñapa Weblogic para evitar error: ProtocolException: Didn't meet stated Content-Length
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		response.setContentType("text/html;charset=UTF-8");
		response.getWriter().write(excepcion.getMessage());
		response.flushBuffer();
		// /////
	}

	/**
	 * Captura error de idioma duplicado en una petición Ajax.
	 * 
	 * @param request Request
	 * @param response Response
	 * @param excepcion Excepción
	 * 
	 * @throws IOException IOException
	 */
	@ExceptionHandler(value = NegocioException.class)
	public void handleNegocioException(HttpServletRequest request, HttpServletResponse response,
			NegocioException excepcion) throws IOException {

		String mensajeError = this.messageSource.getMessage(excepcion.getMessage(), null,
				LocaleContextHolder.getLocale());

		logger.warn("Exception Raised = {}", mensajeError);

		// response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, mensajeError);

		// Ñapa Weblogic para evitar error: ProtocolException: Didn't meet stated Content-Length
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		response.setContentType("text/html;charset=UTF-8");
		response.getWriter().write(mensajeError);
		response.flushBuffer();
		// /////
	}

	/**
	 * Captura las excepciones de validación hechas con la anotación @Valid
	 * 
	 * @param response Response
	 * @param ex Excepción
	 * 
	 * @throws IOException IOException
	 * 
	 */
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public void processValidationError(HttpServletResponse response, MethodArgumentNotValidException ex)
			throws IOException {
		logger.debug("Handling form validation error");

		BindingResult result = ex.getBindingResult();
		List<FieldError> fieldErrors = result.getFieldErrors();

		// Ñapa Weblogic para evitar error: ProtocolException: Didn't meet stated Content-Length
		response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		response.setContentType("text/html;charset=UTF-8");
		response.getWriter().write(processFieldErrors(fieldErrors));
		response.flushBuffer();
		// /////
	}

	/**
	 * Procesa los campos con errores de validación y genera el mensaje de error a mostrar.
	 * 
	 * @param fieldErrors Campos con errores
	 * 
	 * @return mensaje final de error localizado
	 */
	private String processFieldErrors(List<FieldError> fieldErrors) {
		StringBuilder mensajeError = new StringBuilder();

		for (FieldError fieldError : fieldErrors) {
			String localizedErrorMessage = resolveLocalizedErrorMessage(fieldError);
			logger.debug("Adding error message: {} to field: {}", localizedErrorMessage, fieldError.getField());
			mensajeError.append(localizedErrorMessage);
			mensajeError.append("<br/>");
		}

		return mensajeError.toString();
	}

	/**
	 * Localiza el mensaje de error.
	 * 
	 * @param fieldError Campo con error
	 * 
	 * @return mensaje de error localizado
	 */
	private String resolveLocalizedErrorMessage(FieldError fieldError) {
		Locale currentLocale = LocaleContextHolder.getLocale();
		String localizedErrorMessage = this.messageSource.getMessage(fieldError, currentLocale);

		// If a message was not found, return the most accurate field error code instead.
		// You can remove this check if you prefer to get the default error message.
		if (localizedErrorMessage.equals(fieldError.getDefaultMessage())) {
			String[] fieldErrorCodes = fieldError.getCodes();
			localizedErrorMessage = fieldErrorCodes[0];
		}

		return localizedErrorMessage;
	}
}
