package project.web.processor;

import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.chain2.Processing;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts.chain.commands.ActionCommandBase;
import org.apache.struts.chain.contexts.ActionContext;
import org.apache.struts.chain.contexts.ServletActionContext;
import org.apache.struts.config.ActionConfig;

import online.struts.action.UniForm;
import online.struts.chain.command.RequestCommand;
import online.struts.mapping.RequestMapping;

/**
 * 操作ログ出力プロセッサ
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class RequestLogProcessor implements ActionCommandBase {
	/** ログ出力クラス */
	private static final Logger LOGGER = LogManager.getLogger(RequestLogProcessor.class);

	/**
	 * @see org.apache.struts.chain.commands.ActionCommandBase
	 * #execute(org.apache.struts.chain.contexts.ActionContext)
	 */
	@Override
	public Processing execute(final ActionContext<String, Object> context) {

		if (ServletActionContext.class.isInstance(context)) {
			final var sac = ServletActionContext.class.cast(context);

			if (RequestCommand.isFirst(sac, this.getClass())) {
				final var af = sac.getActionForm();
				if (UniForm.class.isInstance(af)) {
					// 開始ログ
					requestLog(sac, UniForm.class.cast(af));
				}
			}
		}

		return Processing.CONTINUE;
	}

	/**
	 * 開始ログ出力処理
	 *
	 * @param sac ServletActionContext
	 * @param form アクションフォーム
	 */
	private void requestLog(final ServletActionContext sac, final UniForm form) {
		final var sj = getHeaderInfo(sac, form);
		// ログ出力区分
		sj.add("type=Request");

		LOGGER.fatal(sj.toString());
	}

	/**
	 * ヘッダ情報（アクションIDまで）取得
	 *
	 * @param sac ServletActionContext
	 * @param uf 汎用フォーム
	 * @return ヘッダ情報
	 */
	private StringJoiner getHeaderInfo(final ServletActionContext sac, final UniForm uf) {
		final var sj = new StringJoiner("\t");
		// セションID
		sj.add("sid=" + getSessionId(sac));
		// ユーザID
		sj.add("ident=" + getUid(sac.getRequest(), uf));
		// IPアドレス
		sj.add("host=" + Objects.toString(uf.getActionParameter().getIp(), ""));
		// 機能ID
		sj.add("fid=" + Objects.toString(getGid(sac.getActionConfig()), ""));
		// アクションID
		sj.add("aid=" + Objects.toString(getAid(uf), ""));
		return sj;
	}

	/**
	 * セションID取得
	 * @param sac ServletActionContext
	 * @return セションID
	 */
	private String getSessionId(final ServletActionContext sac) {
		final var session = sac.getRequest().getSession(false);
		return Optional.ofNullable(session).map(HttpSession::getId).orElse("");
	}

	/**
	 * ユーザID取得
	 *
	 * @param uf 汎用フォーム
	 * @param request HttpServletRequest
	 * @return ユーザID
	 */
	public static String getUid(final HttpServletRequest request, final UniForm uf) {
		String ret = null;
		final var su = uf.getSessionUser();
		if (su != null) {
			ret = su.getUid();
		}
		if (Objects.toString(ret, "").trim().isEmpty()) {
			ret = uf.getString("UID");
		}
		if (Objects.toString(ret, "").trim().isEmpty()) {
			ret = request.getRemoteUser();
		}
		return ret;
	}

	/**
	 * 画面ID取得
	 *
	 * @param ac アクションコンフィグ
	 * @return 画面ID
	 */
	public static String getGid(final ActionConfig ac) {
		return RequestMapping.class.isInstance(ac) ? RequestMapping.class.cast(ac).getGid() : null;
	}

	/**
	 * アクションID取得
	 *
	 * @param uf 汎用フォーム
	 * @return アクションID
	 */
	public static String getAid(final UniForm uf) {
		var ret = uf.getActionParameter().getAid();
		if (Objects.toString(ret, "").trim().isEmpty()) {
			ret = uf.getString("AID");
		}
		return ret;
	}
}
