package project.master;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.stream.Stream;

import core.config.Factory;
import online.model.ModelUtil;
import online.model.UniModel;
import project.common.master.Msg;

/**
 * メッセージUtil
 * @author Tadashi Nakayama
 */
public final class MsgUtil {

	/** ID ERROR */
	public static final String ID_ERROR = "E";
	/** ID WARNING */
	public static final String ID_WARNING = "W";
	/** ID INFORMATION */
	public static final String ID_INFORMATION = "I";

	/**
	 * コンストラクタ
	 */
	private MsgUtil() {
		throw new AssertionError();
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を設定する。
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void putMessage(final UniModel um, final String key,
			final String code, final String... vals) {
		addTopMessage(um, code, vals);
		putItemMessage(um, key, 0, code, vals);
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を設定する。
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param index インデックス
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void putMessage(final UniModel um, final String key, final int index,
			final String code, final String... vals) {
		addTopMessage(um, code, vals);
		putItemMessage(um, key, index, code, vals);
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を設定する。
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param value 値
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void putMessage(final UniModel um, final String key, final String value,
			final String code, final String... vals) {
		addTopMessage(um, code, vals);
		putItemMessage(um, key, value, code, vals);
	}

	/**
	 * メッセージ設定
	 *
	 * @param msg メッセージ
	 * @param sts ステータス
	 * @param um 汎用モデル
	 * @param key キー
	 * @param index インデックス
	 */
	private static void putMessageIndex(final String msg, final String sts,
			final UniModel um, final String key, final int index) {
		final var mkey = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_MESSAGE;
		final var skey = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_STATUS;
		if (um.getArraySize(mkey) <= index) {
			um.setValue(mkey, Arrays.copyOf(um.getStringArray(mkey), index + 1));
			um.setValue(skey, Arrays.copyOf(um.getStringArray(skey), index + 1));
		}

		final var msgArray = um.getStringArray(mkey);
		msgArray[index] = msg;

		final var stsArray = um.getStringArray(skey);
		stsArray[index] = sts;
	}

	/**
	 * メッセージ設定
	 *
	 * @param msg メッセージ
	 * @param sts ステータス
	 * @param um 汎用モデル
	 * @param key キー
	 * @param val チェック対象値
	 */
	private static void putMessageValue(final String msg, final String sts,
			final UniModel um, final String key, final String val) {
		um.addValue(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_MESSAGE, msg);
		um.addValue(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_STATUS, sts);
		um.addValue(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_INVALID, val);
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を設定する。
	 *
	 * @param um 汎用モデル
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void putTopMessage(final UniModel um, final String code, final String... vals) {
		if (!Objects.toString(code, "").isEmpty()) {
			final var msg = Factory.create(Msg.class);
			um.setValue(ModelUtil.TAG_MESSAGE, msg.getMessage(code, vals));
			um.setValue(ModelUtil.TAG_STATUS, msg.getStatus(code));
		}
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を追加する。
	 *
	 * @param um 汎用モデル
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void addTopMessage(final UniModel um, final String code, final String... vals) {
		if (!Objects.toString(code, "").trim().isEmpty()) {
			final var msg = Factory.create(Msg.class);
			um.addValue(ModelUtil.TAG_MESSAGE, msg.getMessage(code, vals));
			um.addValue(ModelUtil.TAG_STATUS, msg.getStatus(code));
		}
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を設定する。
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void putItemMessage(final UniModel um, final String key,
			final String code, final String... vals) {
		putMessage(um, key, 0, code, vals);
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を設定する。
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param index インデックス
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void putItemMessage(final UniModel um, final String key, final int index,
			final String code, final String... vals) {
		if (!Objects.toString(key, "").isEmpty() && !Objects.toString(code, "").isEmpty()) {
			final var msg = Factory.create(Msg.class);
			putMessageIndex(msg.getMessage(code, vals), msg.getStatus(code), um, key, index);
		}
	}

	/**
	 * 引数のメッセージコードからメッセージとステータス両方を設定する。
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param value 値
	 * @param code メッセージコード
	 * @param vals 可変項目
	 */
	public static void putItemMessage(final UniModel um, final String key, final String value,
			final String code, final String... vals) {
		if (!Objects.toString(code, "").isEmpty()) {
			final var msg = Factory.create(Msg.class);
			putMessageValue(msg.getMessage(code, vals), msg.getStatus(code), um, key, value);
		}
	}

	/**
	 * 全メッセージ、ステータス削除
	 * @param um 汎用モデル
	 */
	public static void removeMessage(final UniModel um) {
		for (final var key : new HashSet<>(um.keySet())) {
			if (key.endsWith(ModelUtil.TAG_MESSAGE) || key.endsWith(ModelUtil.TAG_STATUS)) {
				um.remove(key);
			}
		}
	}

	/**
	 * 引数の項目名のメッセージとステータス両方を削除する。
	 *
	 * @param um 汎用モデル
	 */
	public static void removeTopMessage(final UniModel um) {
		um.remove(ModelUtil.TAG_MESSAGE);
		um.remove(ModelUtil.TAG_STATUS);
	}

	/**
	 * 引数の項目名のメッセージとステータス両方を削除する。
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 */
	public static void removeItemMessage(final UniModel um, final String key) {
		if (!Objects.toString(key, "").isEmpty()) {
			um.remove(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_MESSAGE);
			um.remove(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_STATUS);
		}
	}

	/**
	 * メッセージが設定されているか
	 *
	 * @param um 汎用モデル
	 * @return メッセージが設定されている場合 true
	 */
	public static boolean hasTopMessage(final UniModel um) {
		return um.containsKey(ModelUtil.TAG_MESSAGE);
	}

	/**
	 * キー項目に関したメッセージが設定されているか
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @return メッセージが設定されている場合 true
	 */
	public static boolean hasItemMessage(final UniModel um, final String key) {
		return !Objects.toString(key, "").isEmpty()
				&& um.containsKey(key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_MESSAGE);
	}

	/**
	 * キー項目に関したメッセージが設定されているか
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param index インデックス
	 * @return メッセージが設定されている場合 true
	 */
	public static boolean hasItemMessage(final UniModel um, final String key, final int index) {
		final var mkey = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_MESSAGE;
		final var msgs = um.getStringArray(mkey);
		return index < msgs.length && !Objects.toString(msgs[index], "").isEmpty();
	}

	/**
	 * キー項目に関したメッセージが設定されているか
	 *
	 * @param um 汎用モデル
	 * @param key 項目名
	 * @param val チェック対象値
	 * @return メッセージが設定されている場合 true
	 */
	public static boolean hasItemMessage(final UniModel um, final String key, final String val) {
		final var mkey = key + ModelUtil.TAG_SEPARATOR + ModelUtil.TAG_MESSAGE;
		return Stream.of(um.getStringArray(mkey)).anyMatch(m -> Objects.equals(val, m));
	}
}
