package core.exception;

import java.sql.SQLException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * 例外ユーティリティ
 *
 * @author Tadashi Nakayama
 */
public final class ThrowableUtil {

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

	/**
	 * エラーログ出力
	 *
	 * @param th 例外
	 */
	public static void error(final Throwable th) {
		final var logger = LogManager.getLogger(getCallerClassName());
		logger.error(th.getMessage(), th);
		warn(logger, th);
	}

	/**
	 * ワーニングログ出力
	 *
	 * @param val 例外
	 */
	public static void warn(final Throwable val) {
		final var logger = LogManager.getLogger(getCallerClassName());
		warn(logger, val);
	}

	/**
	 * 呼び出し元クラス名取得
	 *
	 * @return 呼び出し元クラス名
	 */
	private static String getCallerClassName() {
		return new Throwable().getStackTrace()[2].getClassName();
	}

	/**
	 * ワーニングログ出力
	 *
	 * @param logger ロガー
	 * @param val 例外
	 */
	private static void warn(final Logger logger, final Throwable val) {
		var cause = val;
		while (cause != null) {
			for (var th = cause; th != null; th = getNextException(th)) {
				logger.warn(th.getMessage());
				if (SQLException.class.isInstance(th)) {
					logger.warn(SQLException.class.cast(th).getSQLState());
					logger.warn(String.valueOf(SQLException.class.cast(th).getErrorCode()));
				}
			}
			cause = cause.getCause();
		}
	}

	/**
	 * 次例外取得
	 *
	 * @param th 例外
	 * @return 次例外
	 */
	public static Throwable getNextException(final Throwable th) {
		try {
			if (th != null) {
				final var mt = th.getClass().getMethod("getNextException");
				return Throwable.class.cast(mt.invoke(th));
			}
		} catch (final ReflectiveOperationException ex) {
			LogManager.getLogger().info(ex.getMessage());
		}
		return null;
	}

	/**
	 * ルート原因例外取得
	 *
	 * @param th 例外
	 * @return ルート原因例外
	 */
	public static Throwable getRootCause(final Throwable th) {
		var ret = th;
		while (ret != null && ret.getCause() != null) {
			ret = ret.getCause();
		}
		return ret;
	}
}
