package online.listener;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * CrossContextRequestListener
 *
 * メインアプリ以外のweb.xmlにこのfilterを設定する。
 * メインアプリ以外のweb.xmlにパラメータmainContextを設定する。
 * Context設定を sessionCookiePath="/" にする。
 * Context設定を crossContext="true" にする。
 *
 * @author Tadashi Nakayama
 */
public class CrossContextRequestListener implements ServletRequestListener {

	/** 主コンテキスト名 */
	private String paramMainContext;

	/**
	 * @see javax.servlet.ServletRequestListener#requestDestroyed(javax.servlet.ServletRequestEvent)
	 */
	@Override
	public void requestDestroyed(final ServletRequestEvent sre) {
		return;
	}

	/**
	 * @see javax.servlet.ServletRequestListener
	 * #requestInitialized(javax.servlet.ServletRequestEvent)
	 */
	@Override
	public void requestInitialized(final ServletRequestEvent sre) {
		setParamsName(sre.getServletContext());
		if (HttpServletRequest.class.isInstance(sre.getServletRequest())) {
			setToSession((HttpServletRequest) sre.getServletRequest());
		}
	}

	/**
	 * セッションに設定する。
	 *
	 * @param request HttpServletRequest
	 * @return セッションに設定した場合 true を返す。
	 */
	private boolean setToSession(final HttpServletRequest request) {
		return updateSessionAttribute(request.getServletContext(), request.getSession(false))
				|| updateSessionAttribute(request.getServletContext(), request.getSession());
	}

	/**
	 * セッションを更新する。
	 *
	 * @param ctx ServletContext
	 * @param session HttpSession
	 * @return セッションに設定した場合 true を返す。
	 */
	private boolean updateSessionAttribute(final ServletContext ctx, final HttpSession session) {
		final Consumer<HttpSession> consumer = s -> {
			final var sc = ctx.getContext(this.paramMainContext);
			final var key = getKeyName(sc, s);
			if (sc.getAttribute(key) == null) {
				if (s.getAttribute(key) != null) {
					s.removeAttribute(key);
				}
			} else {
				if (s.getAttribute(key) == null) {
					s.setAttribute(key, sc.getAttribute(key));
				}
			}
		};

		final var opt = Optional.ofNullable(session);
		opt.ifPresent(consumer);
		return opt.isPresent();
	}

	/**
	 * キー名取得
	 *
	 * @param ctx ServletContext
	 * @param session HttpSession
	 * @return キー名
	 */
	private String getKeyName(final ServletContext ctx, final HttpSession session) {
		var key = Objects.toString(ctx.getInitParameter("loginInfoKey"), "");
		if (key.isEmpty()) {
			key = session.getId();
		}
		return key;
	}

	/**
	 * パラメータ設定
	 *
	 * @param ctx ServletContext
	 */
	private void setParamsName(final ServletContext ctx) {
		if (this.paramMainContext == null) {
			this.paramMainContext = Objects.toString(ctx.getInitParameter("mainContext"), "");
		}
	}
}
