package batch.status;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.util.Unbox;

import common.db.jdbc.Jdbc;
import common.sql.QueryUtil;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;

/**
 * ジョブ詳細管理テーブル取得／更新
 *
 * @author Tadashi Nakayama
 */
public class JobDetailStatusImpl implements JobDetailStatus {

	/**
	 * ジョブ詳細管理状態取得
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param dseq バッチ連番
	 * @return ジョブ詳細管理
	 */
	@Override
	public JobDetail getJobDetail(final Connection conn, final long seq, final int dseq) {
		final var map = new HashMap<String, Object>();
		map.put("JobSeq", seq);
		map.put("BatSeq", dseq);

		final var query = QueryUtil.getSqlFromFile("SelectJobDetailStatus", this.getClass());
		try (var psmt = QueryUtil.createStatement(
				query, map, Jdbc.wrap(conn)::readonlyStatement)) {
			JobDetail dtl = null;
			try (var rs = psmt.executeQuery()) {
				if (rs.next()) {
					dtl = toJobDetailObject(rs);
				}
			}
			return dtl;

		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理状態取得
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return ジョブ詳細管理状態リスト
	 */
	@Override
	public List<JobDetail> selectJobDetails(final Connection conn, final long seq) {
		// 読込
		final var query = QueryUtil.getSqlFromFile("SelectJobDetailStatusList", this.getClass());
		LogManager.getLogger().debug(query);
		LogManager.getLogger().debug("jobseq={}", Unbox.box(seq));

		try (var psmt = QueryUtil.createStatement(query, Collections.singletonMap("JobSeq", seq),
				Jdbc.wrap(conn)::readonlyStatement)) {
			final var list = new ArrayList<JobDetail>();
			try (var rs = psmt.executeQuery()) {
				while (rs.next()) {
					list.add(toJobDetailObject(rs));
				}
			}
			return list;

		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理更新
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param dseq バッチ連番
	 * @param msg メッセージ
	 * @param status ステータス
	 * @param time 日時
	 * @return 更新レコード数
	 */
	@Override
	public int updateJobDetail(final Connection conn, final long seq, final int dseq,
			final String msg, final JobState status, final Timestamp time) {
		final var map = new HashMap<String, Object>();
		map.put("JobSeq", seq);
		map.put("BatSeq", dseq);
		map.put("DateTime", time);
		map.put("Status", status.value());
		map.put("Message", msg);

		final var query = QueryUtil.getSqlFromFile("UpdateJobDetail", this.getClass());
		try (var psmt = QueryUtil.createStatement(
				query, map, Jdbc.wrap(conn)::prepareStatement)) {
			// ジョブ詳細管理テーブル更新
			return psmt.executeUpdate();

		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理新規作成
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param pid プロセスID
	 * @param name バッチ処理名称
	 * @param time 日時
	 * @return バッチシーケンス番号
	 */
	@Override
	public int insertJobDetail(final Connection conn, final long seq,
			final long pid, final String name, final Timestamp time) {

		var dtlno = 1;
		final var dtllst = selectJobDetails(conn, seq);
		if (!dtllst.isEmpty()) {
			dtlno += dtllst.size();
		}

		final var map = new HashMap<String, Object>();
		map.put("JobSeq", seq);
		map.put("BatSeq", dtlno);
		map.put("DateTime", time);
		map.put("Status", JobState.ID_B_EDIT.value());
		map.put("Pid", pid);
		map.put("BatName", name);

		final var query = QueryUtil.getSqlFromFile("InsertJobDetail", this.getClass());
		try (var psmt = QueryUtil.createStatement(
				query, map, Jdbc.wrap(conn)::prepareStatement)) {
			// ジョブ管理テーブル更新
			final var ret = psmt.executeUpdate();
			if (ret != 1) {
				throw new IllegalStateException(String.valueOf(ret));
			}
			return dtlno;

		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理削除
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return 削除件数
	 */
	@Override
	public int deleteJobDetail(final Connection conn, final long seq) {

		final var query = QueryUtil.getSqlFromFile("DeleteJobDetail", this.getClass());
		try (var psmt = QueryUtil.createStatement(query, Collections.singletonMap("JobSeq", seq),
				Jdbc.wrap(conn)::prepareStatement)) {
			return psmt.executeUpdate();
		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細オブジェクト取得
	 *
	 * @param rs 結果セット
	 * @return ジョブ詳細オブジェクト
	 * @throws SQLException SQL例外
	 */
	private JobDetail toJobDetailObject(final ResultSet rs) throws SQLException {
		final var ret = new JobDetail();
		ret.setJobSeq(rs.getLong("JOB_SEQ"));
		ret.setBatSeq(rs.getInt("BAT_SEQ"));
		ret.setPid(rs.getInt("PID"));
		ret.setBatName(rs.getString("BAT_NAME"));
		ret.setBatSts(rs.getInt("BAT_STATUS"));
		ret.setStartDtm(rs.getTimestamp("START_DATE_TIME"));
		ret.setStopDtm(rs.getTimestamp("STOP_DATE_TIME"));
		ret.setMsgTxt(rs.getString("MESSAGE"));
		return ret;
	}
}
