package project.batch;

import java.sql.SQLException;
import java.util.Collections;

import org.apache.logging.log4j.LogManager;

import batch.base.Batch;
import batch.base.BatchProcessor;
import batch.base.BatchProcessorImpl;
import common.db.JdbcSource;
import common.db.jdbc.Jdbc;
import common.sql.QueryUtil;
import core.config.Env;
import core.config.Factory;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;
import project.common.master.Msg;

/**
 * バッチ基盤
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 * @param <T> Type
 */
public final class ProjectPerform<T extends ProjectBatch> implements BatchProcessor {

	/** バッチ処理　開始時メッセージコード */
	public static final String MSGID_START = "ZZ999999001";
	/** バッチ処理　終了時メッセージコード */
	public static final String MSGID_END = "ZZ999999002";
	/** バッチ処理　終了時メッセージコード */
	public static final String MSGID_CANCEL = "ZZ999999004";
	/** バッチ処理　終了時メッセージコード */
	public static final String MSGID_ERROR_END = "ZZ999999003";
	/** バッチ処理　引数不正メッセージコード */
	public static final String MSGID_ERROR_ARG = "ZZ999999005";
	/** バッチ処理　バッチステータス不正メッセージコード */
	public static final String MSGID_ERROR_STS = "ZZ999999006";

	/** ログ出力区分 */
	private static final String KUBUN_START = "BatStart";
	/** ログ出力区分 */
	private static final String KUBUN_END = "BatEnd";

	/** バッチ実行 */
	private final BatchProcessorImpl<T, ProjectParameter> bp;

	/**
	 * コンストラクタ
	 *
	 * @param cls 実行バッチクラス
	 */
	private ProjectPerform(final Class<T> cls) {
		this.bp = new BatchProcessorImpl<>(cls, ProjectParameter.class);
	}

	/**
	 * 開始処理
	 *
	 * @param args 引数
	 * @param cls 実行バッチ
	 * @return 処理結果
	 */
	public static int start(final Class<? extends ProjectBatch> cls, final String... args) {
		return new ProjectPerform<>(cls).execute(args);
	}

	/**
	 * バッチ用メッセージ読込
	 *
	 * @throws SQLException SQL例外
	 */
	private void setMessage() throws SQLException {

		final var msg = Factory.create(Msg.class);
		msg.setPropMessage();

		final var bat = this.bp.getBatch();
		var kid = bat.getKinoID();
		if (9 < kid.codePointCount(0, kid.length())) {
			kid = kid.substring(0, kid.offsetByCodePoints(0, 9));
		}

		try (var conn = JdbcSource.getConnection()) {
			conn.setReadOnly(true);
			conn.setAutoCommit(false);

			final var query = QueryUtil.getSqlFromFile("SelectBatchMessage", this.getClass());
			try (var psmt = QueryUtil.createStatement(
					query, Collections.singletonMap("MsgId", kid),
					Jdbc.wrap(conn)::readonlyStatement)) {
				try (var rs = psmt.executeQuery()) {
					while (rs.next()) {
						msg.setHardMessage(rs.getString(Env.getEnv("Message.MsgId")),
								rs.getString(Env.getEnv("Message.MsgTxt")),
								rs.getString(Env.getEnv("Message.MsgSts")));
					}
				}
			}
		}
	}

	/**
	 * 開始前処理
	 *
	 * @param args 引数
	 * @return 処理結果
	 */
	private int execute(final String... args) {

		var exitCode = Batch.RET_SUCCESS;
		try {
			setMessage();

			// 前処理
			exitCode = preprocess(args);
			if (exitCode == Batch.RET_SUCCESS) {
				// 処理実行
				exitCode = process(args);
			}

		} catch (final PhysicalException ex) {
			LogManager.getLogger().info(ex.getMessage());
			exitCode = Batch.RET_FAILED;
		} catch (final SQLException t) {
			ThrowableUtil.error(t);
			exitCode = Batch.RET_FAILED;
		} finally {
			this.bp.getBatch().printResult();
			// 終了ログ出力
			LogManager.getLogger().fatal(getFinalLog(exitCode));
			exitCode = postprocess(exitCode);
		}

		return exitCode;
	}

	/**
	 * 前処理
	 *
	 * @param params 引数
	 * @return 処理結果
	 */
	@Override
	public int preprocess(final String... params) {
		// 前処理呼び出し
		final var ret = this.bp.preprocess(params);
		if (ret == Batch.RET_SUCCESS) {
			// 開始ログ出力
			final var msg = Factory.create(Msg.class);

			final var bat = this.bp.getBatch();
			final var sb = bat.getLogHeader();
			sb.add(KUBUN_START);
			sb.add(msg.getMessage(MSGID_START, bat.getBatchName()));
			LogManager.getLogger().fatal(sb.toString());
		}
		return ret;
	}

	/**
	 * 実処理
	 *
	 * @param args 引数
	 * @return 処理結果
	 */
	@Override
	public int process(final String... args) {
		return this.bp.process(args);
	}

	/**
	 * 後処理
	 *
	 * @param exitCode 処理結果
	 * @return 処理結果
	 */
	@Override
	public int postprocess(final int exitCode) {
		return this.bp.postprocess(exitCode);
	}

	/**
	 * バッチ実行終了ログ取得
	 *
	 * @param exitCode 終了コード
	 * @return 終了ログ
	 */
	private String getFinalLog(final int exitCode) {
		final var bat = this.bp.getBatch();
		final var sb = bat.getLogHeader().add(KUBUN_END);
		if (exitCode == Batch.RET_SUCCESS || exitCode == Batch.RET_NODATA
				|| exitCode == Batch.RET_WARNING) {
			final var msg = Factory.create(Msg.class);
			sb.add(msg.getMessage(MSGID_END, bat.getBatchName()));
		} else if (exitCode == Batch.RET_CANCELED) {
			final var msg = Factory.create(Msg.class);
			sb.add(msg.getMessage(MSGID_CANCEL, bat.getBatchName()));
		} else {
			final var msg = Factory.create(Msg.class);
			sb.add(msg.getMessage(MSGID_ERROR_END,
					bat.getBatchName(), String.valueOf(exitCode)));
		}
		return sb.toString();
	}
}
