
package batch.controller;

import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;

import javax.rmi.ssl.SslRMIClientSocketFactory;

import org.apache.logging.log4j.LogManager;

import batch.status.Job;
import batch.status.JobMasterInfo;
import batch.status.JobState;
import batch.status.JobStatus;
import common.transaction.XATransaction;
import core.config.Env;
import core.config.Factory;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;
import core.util.DateUtil;

/**
 * ジョブ要求
 *
 * @author Tadashi Nakayama
 */
public final class JobRequestor {

	/** バッチ管理ポート */
	private static final String ENV_BATCH_PORT = "Batch.Port";
	/** バッチ管理SSLポート */
	private static final String ENV_BATCH_SSL_PORT = "Batch.SslPort";

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

	/**
	 * バッチ起動依頼（オンライン）
	 *
	 * @param job ジョブ管理
	 * @return 正常に処理された場合 1以上
	 */
	public static long requestJob(final Job job) {
		final var js = Factory.create(JobStatus.class);

		try (var conn = JobUtil.getConnection()) {
			job.setJobSts(getRequestState(job.getJobId(), conn).value());

			final var seq = js.insertJob(conn, job);
			if (seq <= 0) {
				return 0;
			}

			final var xa = Factory.create(XATransaction.class);
			if (!xa.isInTransaction()) {
				conn.commit();
			}

			return seq;

		} catch (final PhysicalException ex) {
			LogManager.getLogger().warn(ex);
			throw ex;
		} catch (final SQLException ex) {
			ThrowableUtil.error(ex);
			return -1;
		}
	}

	/**
	 * 依頼状態取得
	 *
	 * @param jobid ジョブID
	 * @param conn コネクション
	 * @return 依頼状態
	 */
	private static JobState getRequestState(final String jobid, final Connection conn) {
		var status = JobState.ID_B_IRAI;
		final var jmi = Factory.create(JobMasterInfo.class);
		final var info = jmi.getJobMasterInfo(conn, jobid);
		if (info != null) {
			// 現在時刻取得
			final var nowstr = DateUtil.format(new Date(), "HHmm");
			if (!JobUtil.checkInTime(nowstr, info.getRunnableBegin(), info.getRunnableEnd())) {
				status = JobState.ID_B_WAIT;
			}
		}
		return status;
	}

	/**
	 * 処理中止処理
	 *
	 * @param jobseq ジョブ連番
	 */
	public static void cancelJob(final long jobseq) {
		final var hostId = getHostId(jobseq);
		if (hostId != null) {
			cancel(jobseq, hostId);
		}
	}

	/**
	 * ホストID取得
	 *
	 * @param jobseq ジョブ連番
	 * @return ホストID
	 */
	private static String getHostId(final long jobseq) {
		try (var conn = JobUtil.getConnection()) {
			final var js = Factory.create(JobStatus.class);
			final var job = js.getJob(conn, jobseq);
			return (job != null) ? job.getHostId() : null;
		}
	}

	/**
	 * キャンセル呼出
	 *
	 * @param jobseq ジョブ連番
	 * @param hostId ホストID
	 */
	private static void cancel(final long jobseq, final String hostId) {

		final var lookup = toBatchUri("rmi://" + hostId);
		LogManager.getLogger().debug("cancel lookup={}", lookup);

		try {
			final JobManager jm;
			final var ssl = getSslPort();
			if (0 < ssl) {
				final Registry registry = LocateRegistry.getRegistry(
								hostId, ssl, new SslRMIClientSocketFactory());
				jm = JobManager.class.cast(registry.lookup(lookup));
			} else {
				jm = JobManager.class.cast(java.rmi.Naming.lookup(lookup));
			}
			jm.cancel(jobseq);

		} catch (final NotBoundException | MalformedURLException ex) {
			LogManager.getLogger().error(ex.getMessage(), ex);
			throw new PhysicalException(ex);
		} catch (final RemoteException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブURI取得
	 *
	 * @param host ホスト部
	 * @return ジョブURI
	 */
	static String toBatchUri(final String host) {
		var port = "";
		if (0 < Env.getEnv(ENV_BATCH_PORT, 0) && getSslPort() <= 0) {
			port = ":" + Env.getEnv(ENV_BATCH_PORT);
		}
		return host + port + JobManager.BATCH_BIND_NAME;
	}

	/**
	 * SSLポート番号取得
	 *
	 * @return ポート番号
	 */
	static int getSslPort() {
		return Env.getEnv(ENV_BATCH_SSL_PORT, -1);
	}
}
