package com.style24.core.support.controller;

import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException;
import java.sql.SQLRecoverableException;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.ibatis.binding.BindingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.core.env.Environment;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.style24.core.biz.thirdparty.KollusApi;
import com.style24.core.support.message.TscMessageByLocale;

import lombok.extern.slf4j.Slf4j;

import com.gagaframework.web.rest.exception.GagaRestException;
import com.gagaframework.web.rest.server.GagaResponse;
import com.gagaframework.web.rest.server.GagaResponseStatus;

/**
 * Controller Advice
 * 
 * @author gagamel
 * @since 2020. 10. 5
 */
@ControllerAdvice
@Slf4j
public class TscBaseController {

	@Autowired
	private Environment env;

	@Autowired
	private TscMessageByLocale message;

	/**
	 * Json data 형식 외의 모든 문자열의 앞, 뒤 공백 제거
	 * Json data 형식의 모든 문자열의 앞, 뒤 공백 제거 시는 TsaStringTrim에서 처리 (@RequestBody annotation 이용 시)
	 * 
	 * @param binder
	 */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		StringTrimmerEditor stringtrimmer = new StringTrimmerEditor(true);
//		stringtrimmer.getAsText().replaceAll("<", "&lt;").replaceAll(">", "&gt;");
		binder.registerCustomEditor(String.class, stringtrimmer);
	}

	@ModelAttribute("env")
	public Environment getEnvironment() {
		return env;
	}

	@ModelAttribute("kollusMediaUrl")
	public String getMediaUrl() {
		return KollusApi.mediaUrl;
	}

	@ResponseBody
	public GagaResponse ok() {
		return GagaResponse.of(GagaResponseStatus.SUCCESS.getCode(), "SUCCESS");
	}

	@ResponseBody
	public GagaResponse ok(String message) {
		return GagaResponse.of(GagaResponseStatus.SUCCESS.getCode(), message);
	}

	@ResponseBody
	public GagaResponse error(String message) {
		return GagaResponse.error(GagaResponseStatus.FAIL.getCode(), message);
	}

//	@ExceptionHandler(AccessDeniedException.class)
//	@ResponseBody
//	public GagaResponse handleForbidden(Exception ex) {
//		return GagaResponse.error(GagaResponseStatus.FORBIDDEN.getCode(), ex.getMessage());
//	}
//
//	@ExceptionHandler(TypeMismatchException.class)
//	@ResponseBody
//	public GagaResponse handleBadRequestException(Exception ex) {
//		errorLogging(ex);
//		return GagaResponse.error(GagaResponseStatus.BAD_REQUEST.getCode(), ex.getMessage());
//	}
//
//	@ExceptionHandler(MissingServletRequestParameterException.class)
//	@ResponseBody
//	public GagaResponse handleRequestParameterException(MissingServletRequestParameterException ex) {
//		errorLogging(ex);
//		return GagaResponse.error(GagaResponseStatus.BAD_REQUEST.getCode(), ex.getMessage());
//	}

	/**
	 * HttpRequestMethodNotSupportedException Handler
	 * 
	 * @param e - HttpRequestMethodNotSupportedException, SQLRecoverableException
	 * @return forwarding URI
	 */
	@ExceptionHandler({HttpRequestMethodNotSupportedException.class, SQLRecoverableException.class})
	public String handleException(HttpRequestMethodNotSupportedException ex) {
		return "forward:/error";
	}

	@ExceptionHandler(Throwable.class)
	@ResponseBody
	public GagaResponse handleException(Throwable throwable) {
		errorLogging(throwable);

		Throwable rootCause = ExceptionUtils.getRootCause(throwable);

		if (rootCause != null) {
			throwable = rootCause;
		}

		if (throwable instanceof SQLException || throwable instanceof BindingException
			|| throwable instanceof JsonMappingException
			|| throwable instanceof UndeclaredThrowableException) {
			String message = String.format("데이터 처리중 에러가 발생하였습니다. 시스템 관리자에게 문의하세요.");
			return GagaResponse.error(GagaResponseStatus.INTERNAL_SERVER_ERROR.getCode(), message);
		}

		return GagaResponse.error(GagaResponseStatus.INTERNAL_SERVER_ERROR.getCode(), throwable.getMessage());
	}

	private void errorLogging(Throwable throwable) {
		if (log.isErrorEnabled()) {
			Throwable rootCause = ExceptionUtils.getRootCause(throwable);

			if (rootCause != null) {
				throwable = rootCause;
			}

			if (throwable.getMessage() != null) {
				log.error(throwable.getMessage(), throwable);
			} else {
				log.error("ERROR", throwable);
			}
		}
	}

//	@ExceptionHandler(MethodArgumentNotValidException.class)
//	@ResponseBody
//	public Object processValidationError(MethodArgumentNotValidException ex) {
//		FieldError fieldError = ex.getBindingResult().getFieldErrors().get(0);
//		GagaResponse error = GagaResponse.error(GagaResponseStatus.INTERNAL_SERVER_ERROR.getCode(), fieldError.getDefaultMessage());
//		error.getError().setRequiredKey(fieldError.getField());
//		return error;
//	}

	/**
	 * Custom Validation using group class
	 * 
	 * @param domain - Domain to validate
	 * @param groupClass - Group class to validate
	 */
	public void customValidation(Object clazz, Class<?> groupClass) {
		Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

		Set<ConstraintViolation<Object>> violations = validator.validate(clazz, groupClass);

		for (ConstraintViolation<?> violation : violations) {
			String validationMessage = violation.getMessage();
			validationMessage = StringUtils.removeStart(validationMessage, "{");
			validationMessage = StringUtils.removeEnd(validationMessage, "}");
			throw new GagaRestException(GagaResponseStatus.FAIL.getCode(), message.getMessage(validationMessage));
		}
	}

//	@ExceptionHandler(TscNoSessionException.class)
//	public String noSessionException(TscNoSessionException ex) {
//		return "forward:/error/noSession";
//	}

}
