package com.ejie.ad74a.scheduler;

import java.util.Date;
import java.util.HashMap;

import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Programador de trabajos
 * 
 * @author IBERMATICA
 *
 */
@Service(value = "jobSchedulerService")
public class JobSchedulerImpl implements JobSchedulerService {

	@Autowired
	private Scheduler scheduler;

	private static final Logger logger = LoggerFactory.getLogger(JobSchedulerImpl.class);

	/**
	 * Programa la ejecución de cualquier trabajo
	 * 
	 * @param trabajo SchedulerJobType con la tipología del trabajo a programar
	 * @param params  HashMap con los parámetros necesarios para su ejecución
	 */
	@Override
	public void programarEjecucion(SchedulerJobType trabajo, HashMap<String, String> params) {
		this.programarEjecucion(trabajo, params, null);
	}

	/**
	 * Programa la ejecución de cualquier trabajo
	 * 
	 * @param trabajo SchedulerJobType con la tipología del trabajo a programar
	 * @param params  HashMap con los parámetros necesarios para su ejecución
	 * @params delayMS Long Retardo en ms. de la ejecución del trabajo (null para
	 *         configurarlo por defecto)
	 */
	@Override
	public void programarEjecucion(SchedulerJobType trabajo, HashMap<String, String> params, Long delayMS) {
		JobSchedulerImpl.logger.info("JobScheduler.programarEjecucion() - :" + trabajo.getTipoTrabajo() + " - "
				+ trabajo.getDescripcion() + "- INICIO");

		// ** Generamos el Job ** //
		JobDetail jobDetail = this.buildJobDetail(trabajo, params);
		JobSchedulerImpl.logger.info("JobScheduler.programarEjecucion() - Detalle de trabajo creado.");

		// ** Trigger con la ejecución **//
		Trigger trigger = this.buildJobTrigger(jobDetail, trabajo, (delayMS == null ? trabajo.getDelayMS() : delayMS));
		JobSchedulerImpl.logger.info("JobScheduler.programarEjecucion() - Trigger generado.");

		// ** Programación del trabajo ** //
		try {
			scheduler.scheduleJob(jobDetail, trigger);
			JobSchedulerImpl.logger.info("JobScheduler.programarEjecucion() - Ejecución programada. - FIN");

		} catch (SchedulerException e) {
			JobSchedulerImpl.logger
					.error("JobScheduler.programarEjecucion() ERROR programando ejecución :" + e.getMessage(), e);

		}
	}

	/**
	 * Construye el trabajo (Job) a partir de los datos
	 * 
	 * @param trabajo SchedulerJobType Tipología del trabajo
	 * @param params  HashMap con los parámetros necesarios para ejecutar el trabajo
	 * @return JobDetail
	 */
	private JobDetail buildJobDetail(SchedulerJobType trabajo, HashMap<String, String> params) {
		JobDataMap jobDataMap = new JobDataMap();

		for (String clave : params.keySet()) {
			jobDataMap.put(clave, params.get(clave));
		}

		return JobBuilder.newJob(GestionErrorJob.class)
				.withIdentity(trabajo.getTipoTrabajo().concat("_").concat((String) jobDataMap.get("id")),
						("AD74-").concat(trabajo.getTipoTrabajo()).concat("-GROUP"))
				.withDescription(trabajo.getDescripcion().concat(" " + (String) jobDataMap.get("id")))
				.usingJobData(jobDataMap).storeDurably(false).build();
	}

	/**
	 * Programa el trigger de ejecución
	 * 
	 * @param jobDetail jpobDetail con la información del trabajo
	 * @param trabajo   SchedulerJobType Tipo de Trabajo
	 * @param delayms   Long retardo en ms
	 * @return Trigger
	 */
	private Trigger buildJobTrigger(JobDetail jobDetail, SchedulerJobType trabajo, long delayms) {
		// ** Cálculo de fecha de ejecución según delay ** //
		long timeInSecs = System.currentTimeMillis();
		Date startAtDate = new Date(timeInSecs + delayms);

		return TriggerBuilder.newTrigger().forJob(jobDetail)
				.withIdentity(jobDetail.getKey().getName(),
						("AD74-").concat(trabajo.getTipoTrabajo()).concat("-TRIGGERS"))
				.withDescription(trabajo.getDescripcion().concat(" " + (String) jobDetail.getJobDataMap().get("id")))
				.startAt(startAtDate)
				.withSchedule(SimpleScheduleBuilder.simpleSchedule().withMisfireHandlingInstructionFireNow()).build();
	}

}
