package online.struts.action;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Locale;

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

import org.apache.logging.log4j.LogManager;

import common.db.ExclusiveException;
import core.exception.LogicalException;
import core.exception.PhysicalException;
import online.filter.FilterUtil;
import online.struts.mapping.RequestMapping;

/**
 * Restアクション
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public interface RestAction extends PerformAction {

	/**
	 * @see online.struts.action.PerformAction
	 * #perform(online.struts.mapping.RequestMapping, online.struts.action.UniForm,
	 * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	default String perform(final RequestMapping mapping, final UniForm uf,
			final HttpServletRequest request, final HttpServletResponse response) {

		FilterUtil.setNoRedirect(request);

		final Method mt = getMethod(request.getMethod());
		if (mt == null) {
			try {
				if (!response.isCommitted()) {
					response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
					response.flushBuffer();
				}
			} catch (final IOException ex) {
				LogManager.getLogger().info(ex.getMessage());
			}
			return null;
		}

		try {
			response.reset();
			return String.class.cast(mt.invoke(this, Arrays.copyOf(
					new Object[]{uf, response, request, mapping}, mt.getParameterCount())));

		} catch (final IllegalAccessException ex) {
			LogManager.getLogger().error(ex.getMessage(), ex);
			throw new PhysicalException(ex);
		} catch (final InvocationTargetException ex) {
			final var e = ex.getCause();
			if (LogicalException.class.isInstance(e)) {
				LogManager.getLogger().info(e.getMessage());
				try {
					var status = LogicalException.class.cast(e).getStatusCode();
					if (status == 0) {
						status = HttpServletResponse.SC_BAD_REQUEST;
					}
					if (!response.isCommitted()) {
						response.sendError(status, e.getMessage());
						response.flushBuffer();
					}
				} catch (final IOException ioe) {
					LogManager.getLogger().info(ioe.getMessage());
				}
				return null;
			} else if (ExclusiveException.class.isInstance(e)) {
				throw ExclusiveException.class.cast(e);
			} else if (IOException.class.isInstance(e)) {
				LogManager.getLogger().info(e.getMessage());
				return null;
			} else if (RuntimeException.class.isInstance(e)) {
				throw RuntimeException.class.cast(e);
			}
			throw new PhysicalException(e);
		}
	}

	/**
	 * メソッド取得
	 *
	 * @param method リクエストメソッド
	 * @return メソッド
	 */
	private Method getMethod(final String method) {
		final String name = "HEAD".equalsIgnoreCase(method) ? "GET" : method;
		return RequestMapping.getActionMethod(this.getClass(), name.toLowerCase(Locale.ENGLISH));
	}
}
