package eu.dnetlib.openaire.common;

import java.util.List;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import eu.dnetlib.enabling.datasources.common.DsmException;
import eu.dnetlib.enabling.datasources.common.DsmForbiddenException;
import eu.dnetlib.enabling.datasources.common.DsmNotFoundException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * Created by claudio on 18/07/2017.
 */
public abstract class AbstractExporterController {

	private static final Log log = LogFactory.getLog(AbstractExporterController.class); // NOPMD by marko on 11/24/08 5:02 PM

	@ResponseBody
	@ExceptionHandler({DsmException.class})
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	public ErrorMessage handleDSMException(final Exception e) {
		return _handleError(e);
	}

	@ResponseBody
	@ExceptionHandler(DsmForbiddenException.class)
	@ResponseStatus(value = HttpStatus.FORBIDDEN)
	public ErrorMessage handleForbiddenException(final Exception e) {
		return _handleError(e);
	}

	@ResponseBody
	@ExceptionHandler({DsmNotFoundException.class})
	@ResponseStatus(value = HttpStatus.NOT_FOUND)
	public ErrorMessage handleNotFoundException(final Exception e) {
		return _handleError(e);
	}

	@ResponseBody
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public List<ErrorMessage> processValidationError(final MethodArgumentNotValidException e) {
		return e.getBindingResult()
				.getFieldErrors().stream()
					.map(fe -> new ErrorMessage(
							String.format("field '%s'", fe.getField()),
							String.format("rejected value '%s'", fe.getRejectedValue()),
							fe.getDefaultMessage()))
					.collect(Collectors.toList());
	}

	private ErrorMessage _handleError(final Exception e) {
		log.error(e);
		if (StringUtils.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(e), "Broken pipe")) {
			return null;        //socket is closed, cannot return any response
		} else {
			return new ErrorMessage(e);
		}
	}

	@JsonAutoDetect
	public class ErrorMessage {

		private final String message;
		private final String details;
		private final String stacktrace;

		public ErrorMessage(final Exception e) {
			this(e.getMessage(), "", ExceptionUtils.getStackTrace(e));
		}

		public ErrorMessage(final String message, final String details, final String stacktrace) {
			this.message = message;
			this.details = details;
			this.stacktrace = stacktrace;
		}

		public String getMessage() {
			return this.message;
		}

		public String getStacktrace() {
			return this.stacktrace;
		}

		public String getDetails() {
			return details;
		}
	}

}
