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 online.model.ModelUtil;
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 ResponseLogProcessor implements ActionCommandBase {
	/** ログ出力クラス */
	private static final Logger LOGGER = LogManager.getLogger(ResponseLogProcessor.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);

			final var af = sac.getForwardConfig();
			if (af == null || RequestMapping.isJsp(af) || RequestMapping.isRedirect(af)) {
				if (RequestCommand.isFirst(sac, this.getClass())) {
					final var form = context.getActionForm();
					if (UniForm.class.isInstance(form)) {
						// 終了ログ
						responseLog(sac, UniForm.class.cast(form));
					}
				}
			}
		}

		return Processing.CONTINUE;
	}

	/**
	 * 終了ログ出力処理
	 *
	 * @param sac ServletActionContext
	 * @param uf アクションフォーム
	 */
	private void responseLog(final ServletActionContext sac, final UniForm uf) {
		final var sj = getHeaderInfo(sac, uf);
		// ログ出力区分
		sj.add("type:Response");
		getMessageInfo(sj, sac.getRequest(), uf);

		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:" + RequestLogProcessor.getUid(sac.getRequest(), uf));
		// IPアドレス
		sj.add("host:" + Objects.toString(uf.getActionParameter().getIp(), ""));
		// 機能ID
		sj.add("fid:" + Objects.toString(RequestLogProcessor.getGid(sac.getActionConfig()), ""));
		// アクションID
		sj.add("aid:" + Objects.toString(RequestLogProcessor.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("");
	}

	/**
	 * メッセージ情報設定
	 *
	 * @param sj 設定先
	 * @param request HTTPリクエスト
	 * @param uf 汎用フォーム
	 */
	private void getMessageInfo(final StringJoiner sj,
			final HttpServletRequest request, final UniForm uf) {
		// メッセージ追加
		sj.add(String.join(",", uf.getStringArray(ModelUtil.TAG_MESSAGE)));

		// 項目メッセージ追加
		for (final var name : request.getParameterMap().keySet()) {
			final var key = name + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_MESSAGE;
			if (uf.containsKey(key)) {
				sj.add(name + ":" + uf.getString(key));
			}
		}
	}
}
