package online.struts.action;

import java.io.IOException;

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

import online.filter.FilterUtil;
import online.struts.mapping.RequestMapping;

import org.apache.logging.log4j.LogManager;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ExceptionHandler;
import org.apache.struts.chain.commands.InvalidPathException;
import org.apache.struts.config.ExceptionConfig;

import core.exception.LogicalException;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;

/**
 * 例外ハンドラ
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class ThrowableHandler extends ExceptionHandler {

	/**
	 * @see org.apache.struts.action.ExceptionHandler
	 * #execute(java.lang.Exception, org.apache.struts.config.ExceptionConfig,
	 * org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
	 * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	public ActionForward execute(final Exception ex,
			final ExceptionConfig ae, final ActionMapping mapping,
			final ActionForm form, final HttpServletRequest request,
			final HttpServletResponse response) {
		if (InvalidPathException.class.isInstance(ex)
				|| InvalidPathException.class.isInstance(ex.getCause())) {
			LogManager.getLogger().info(ex.getMessage());
		} else if (!PhysicalException.class.isInstance(ex)
				&& !LogicalException.class.isInstance(ex)) {
			ThrowableUtil.error(ex);
		}

		// エラーステータスを返しても、キャッシュ状態においてもキャッシュされないため、
		// 同一URLの正常画面が履歴に存在すると、画面を前後した場合それが表示されてしまう。
		// REST時のみ設定
		try {
			if (!response.isCommitted() && isRestAction(mapping)) {
				response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
				response.flushBuffer();
			}
		} catch (final IOException e) {
			LogManager.getLogger().info(e.getMessage());
		}

		final var af = super.execute(ex, ae, mapping, form, request, response);
		if (af.getPath() != null) {
			final var redirect = ae.getProperty("redirect");
			if (redirect != null) {
				af.setRedirect(Boolean.parseBoolean(redirect));
			} else if (FilterUtil.isPostMethod(request.getMethod())) {
				af.setRedirect(true);
			}
		}
		return af;
	}

	/**
	 * RESTアクション判断
	 *
	 * @param mapping マッピング
	 * @return RESTアクション時 true を返す。
	 */
	private boolean isRestAction(final ActionMapping mapping) {
		return RequestMapping.class.isInstance(mapping)
				&& RequestMapping.class.cast(mapping).isRestAction();
	}
}
