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.JobMaster;
import batch.status.JobMasterInfo;
import batch.status.JobState;
import batch.status.JobStatus;
import common.db.jdbc.Jdbc;
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
 * @version 1.0.0
 */
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 JobStatus js = Factory.create(JobStatus.class);

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

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

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

			return ret;

		} catch (final PhysicalException 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) {
		JobState status = JobState.ID_B_IRAI;
		final JobMasterInfo ji = Factory.create(JobMasterInfo.class);
		final JobMaster info = ji.getJobMasterInfo(conn, jobid);
		if (info != null) {
			// 現在時刻取得
			final String 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 String hostId = getHostId(jobseq);
		if (hostId != null) {
			cancel(jobseq, hostId);
		}
	}

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

	/**
	 * キャンセル呼出
	 * @param jobseq ジョブ連番
	 * @param hostId ホストID
	 */
	private static void cancel(final long jobseq, final String hostId) {
		try {
			JobManager jm = null;
			final int ssl = JobRequestor.getSslPort();
			if (0 < ssl) {
				final Registry registry = LocateRegistry.getRegistry(
								hostId, ssl, new SslRMIClientSocketFactory());
				jm = JobManager.class.cast(registry.lookup(
						"rmi://" + hostId + JobManager.BATCH_BIND_NAME));
			} else {
				final String lookup = toBatchUri("rmi://" + hostId, JobManager.BATCH_BIND_NAME);
				LogManager.getLogger().debug("cancel lookup={}", lookup);

				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 ホスト部
	 * @param path パス部
	 * @return ジョブURI
	 */
	static String toBatchUri(final String host, final String path) {
		String port = "";
		if (0 < Env.getEnv(ENV_BATCH_PORT, 0)) {
			port = ":" + Env.getEnv(ENV_BATCH_PORT);
		}
		return host + port + path;
	}

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