/*******************************************************************************
 * Copyright © 2005 VHA. All rights reserved
 ******************************************************************************/
package gov.va.med.esr.ui.admin.action;

// Java Classes
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;

import gov.va.med.esr.common.batchprocess.BatchProcessTriggerGroups;
import gov.va.med.esr.ui.common.action.AbstractAction;
import gov.va.med.fw.batchprocess.BatchProcessDetail;
import gov.va.med.fw.batchprocess.BatchProcessDetailStatus;
import gov.va.med.fw.batchprocess.BatchProcessInvoker;
import gov.va.med.fw.batchprocess.BatchProcessService;
import gov.va.med.fw.batchprocess.model.JobConfig;
import gov.va.med.fw.batchprocess.model.JobResult;
import gov.va.med.fw.batchprocess.model.JobStatus;
import gov.va.med.fw.model.EntityKeyFactory;
import gov.va.med.fw.scheduling.SchedulingService;
import gov.va.med.fw.scheduling.TriggerStatus;

public class BatchProcessAction extends AbstractAction {
	// Struts forwards
	public static final String BATCH_PROCESS_MGMT = "batchProcessManagement";
	public static final String BATCH_PROCESS_ACTIVE = "batchProcessActive";
	public static final String BATCH_PROCESS_CONFIG = "batchProcessConfig";
	public static final String BATCH_PROCESS_EXECUTE_WITH_ARGS = "batchProcessExecuteWithArgs";
	public static final String BATCH_PROCESS_HISTORY = "batchProcessHistory";

	public static final String BATCH_PROCESS_MGMT_FAILURE = "batchProcessManagement.failure";
	
	private BatchProcessInvoker invoker;
	
	private SchedulingService schedulingService;
	
	private BatchProcessService batchProcessService;

	public ActionForward displayActiveJobs(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception { 		
		List activeJobs = batchProcessService.getJobResults(JobStatus.IN_PROCESS);
		if(activeJobs != null && !activeJobs.isEmpty())
			request.setAttribute("activeJobs", activeJobs);
		return mapping.findForward(BATCH_PROCESS_ACTIVE);
	}

	public ActionForward markActiveJobAsCancelled(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		DynaActionForm actionForm = (DynaActionForm) form;
		String jobResultId = actionForm.getString("jobResultId");
		batchProcessService.cancelActiveJob(EntityKeyFactory.createEntityKey(jobResultId, JobResult.class));
		
		return displayActiveJobs(mapping, form, request, null);
	}

	public ActionForward markActiveJobAsError(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception { 
		DynaActionForm actionForm = (DynaActionForm) form;
		String jobResultId = actionForm.getString("jobResultId");
		batchProcessService.errActiveJob(EntityKeyFactory.createEntityKey(jobResultId, JobResult.class));

		return displayActiveJobs(mapping, form, request, null);
	}
	
	public ActionForward displayManagementDashboard(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		Collection jobs = getLookupCacheService().getFromCache(BatchProcessDetail.class.getName());
		List jobAndTriggerStatus = new ArrayList(jobs.size());
		// now iterate through and determine the dynamic pieces from the Scheduler....next Fire time, etc
		Iterator itr = jobs != null ? jobs.iterator() : null;
		BatchProcessDetail detail = null;
		BatchProcessDetailStatus status = null;
		TriggerStatus triggerStatus = null;
		while(itr != null && itr.hasNext()) {
			detail = (BatchProcessDetail) itr.next();
			status = new BatchProcessDetailStatus();
			status.setBatchProcessDetail(detail);
			// call Scheduler to get trigger status
			triggerStatus = schedulingService.getTriggerStatus(detail.getTriggerName(), detail.getTriggerGroup());
			status.setTriggerStatus(triggerStatus);
			jobAndTriggerStatus.add(status);
		}
		Collections.sort(jobAndTriggerStatus);
		request.setAttribute("jobs", jobAndTriggerStatus);
		return mapping.findForward(BATCH_PROCESS_MGMT);
	}

	/**
	 * Initiates the batch job.
	 * 
	 * @param mapping
	 *            Struts action mapping for this action
	 * @param form
	 *            Struts form bean for this action
	 * @param request
	 *            The Http Request
	 * @param response
	 *            The Http Response
	 * 
	 * @return A struts action forward for where we will go next.
	 * @throws Exception
	 *             If there are any errors during processing.
	 */
	public ActionForward executeBatchProcess(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		DynaActionForm actionForm = (DynaActionForm) form;
		String jobName = actionForm.getString("jobName");
				
		BatchProcessDetail batchProcessDetail = (BatchProcessDetail) getLookupCacheService().getByCodeFromCache(BatchProcessDetail.class.getName(), jobName);
		if(batchProcessDetail == null)
			return mapping.findForward(BATCH_PROCESS_MGMT_FAILURE);
		
		if ( batchProcessDetail.isExecutableFromManagementPage() == false ) {
			return mapping.findForward(BATCH_PROCESS_MGMT_FAILURE);
		}
		
		
		invoker.invokeBatchProcessWithEvent(batchProcessDetail);
		addInformationMessage(request, "message.batchProcessManagement.execute.success", jobName);
		
		return displayManagementDashboard(mapping, form, request, response);
	}
	
	public ActionForward updateConfig(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		DynaActionForm actionForm = (DynaActionForm) form;
		String jobName = actionForm.getString("jobName");
		String jobGroup = actionForm.getString("jobGroup");
		
		JobConfig jobConfig = batchProcessService.getJobConfig(jobName, jobGroup);
		if(jobConfig == null) {
			jobConfig = new JobConfig();
			jobConfig.setJobName(jobName);
			jobConfig.setJobGroup(jobGroup);
		}
			
		jobConfig.setEmailDistributionList(actionForm.getString("emailDistributionList"));
		batchProcessService.saveJobConfig(jobConfig);
		
		if(!actionForm.get("previousTriggerStatus").equals("triggerStatus")) {
			if(TriggerStatus.NORMAL_STATUS.equals(actionForm.get("triggerStatus")))
				schedulingService.resumeJob(jobName, jobGroup);
			else if(TriggerStatus.PAUSED_STATUS.equals(actionForm.get("triggerStatus")))
				schedulingService.pauseJob(jobName, jobGroup);			
		}
		addInformationMessage(request, "message.batchProcessConfig.success", jobName);
		
		return displayManagementDashboard(mapping, form, request, response);
	}	

	public ActionForward executeBatchProcessWithArgs(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		DynaActionForm actionForm = (DynaActionForm) form;
		String jobName = actionForm.getString("jobName");

		Object[] invocationArgs = null;
		String invocationArgsStrArray = actionForm.getString("invocationArgs");
		if(StringUtils.isNotBlank(invocationArgsStrArray)) {
			invocationArgs = invocationArgsStrArray.split(" ");
		}
		
		BatchProcessDetail batchProcessDetail = (BatchProcessDetail) getLookupCacheService().getByCodeFromCache(BatchProcessDetail.class.getName(), jobName);
		if(batchProcessDetail == null)
			return mapping.findForward(BATCH_PROCESS_MGMT_FAILURE);
		
		if ( batchProcessDetail.isExecutableFromManagementPage() == false ) {
			return mapping.findForward(BATCH_PROCESS_MGMT_FAILURE);
		}
		
		invoker.invokeBatchProcessWithEvent(batchProcessDetail, invocationArgs);
		addInformationMessage(request, "message.batchProcessManagement.execute.success", jobName);
		
		return displayManagementDashboard(mapping, form, request, response);
	}
	
	public ActionForward pauseAll(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		schedulingService.pauseTriggerGroup(BatchProcessTriggerGroups.STATIC_BATCH_JOBS);
		addInformationMessage(request, "message.batchProcessManagement.pauseAll.success");
		
		return displayManagementDashboard(mapping, form, request, response);
	}

	public ActionForward resumeAll(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		schedulingService.resumeTriggerGroup(BatchProcessTriggerGroups.STATIC_BATCH_JOBS);
		addInformationMessage(request, "message.batchProcessManagement.resumeAll.success");
		
		return displayManagementDashboard(mapping, form, request, response);
	}

	public ActionForward viewHistory(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		BatchProcessDetailStatus job = initBatchProcessDetailStatus((DynaActionForm) form, request);
		List jobResults = batchProcessService.getFinishedJobResults(job.getBatchProcessDetail().getJobName(),
				job.getBatchProcessDetail().getJobGroup());
		if(jobResults != null && !jobResults.isEmpty())
			request.setAttribute("jobResults", jobResults);
		return mapping.findForward(BATCH_PROCESS_HISTORY);
	}

	public ActionForward viewExecuteWithArgs(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		initBatchProcessDetailStatus((DynaActionForm) form, request);
		return mapping.findForward(BATCH_PROCESS_EXECUTE_WITH_ARGS);
	}
	
	public ActionForward viewConfig(ActionMapping mapping,
			ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		DynaActionForm actionForm = (DynaActionForm) form;
		BatchProcessDetailStatus job = initBatchProcessDetailStatus((DynaActionForm) form, request);
		
		// TODO: there is gotta be a better way to do this....
		Collection triggerStatuses = (Collection) request.getSession().getServletContext().getAttribute("triggerStatuses");
		if(triggerStatuses == null) {
			triggerStatuses = new ArrayList();
			triggerStatuses.add(TriggerStatus.NORMAL_STATUS);
			triggerStatuses.add(TriggerStatus.PAUSED_STATUS);
			request.getSession().getServletContext().setAttribute("triggerStatuses", triggerStatuses);
		}
		actionForm.set("triggerStatus", job.getTriggerStatus().getStatus());
		actionForm.set("previousTriggerStatus", job.getTriggerStatus().getStatus());
		
		// update with current JobConfig data
		JobConfig config = batchProcessService.getJobConfig(job.getBatchProcessDetail().getJobName(),
				job.getBatchProcessDetail().getJobGroup());
		if(config != null) {
			actionForm.set("emailDistributionList", config.getEmailDistributionList());
		}		
		return mapping.findForward(BATCH_PROCESS_CONFIG);
	}
	
	private BatchProcessDetailStatus initBatchProcessDetailStatus(DynaActionForm form, HttpServletRequest request) throws Exception {
		String jobName = form.getString("jobName");
		
		BatchProcessDetailStatus status = new BatchProcessDetailStatus();
		BatchProcessDetail detail = (BatchProcessDetail) getLookupCacheService().getByCodeFromCache(BatchProcessDetail.class.getName(), jobName);		
		status.setBatchProcessDetail(detail);
		// call Scheduler to get trigger status
		TriggerStatus triggerStatus = schedulingService.getTriggerStatus(detail.getTriggerName(), detail.getTriggerGroup());
		status.setTriggerStatus(triggerStatus);
		
		request.setAttribute("job", status);
		return status;
	}
	
	
	/**
	 * Initialize and get the Struts lookup dispatch method map hashmap.
	 * 
	 * @return the method map
	 * @see org.apache.struts.actions.LookupDispatchAction#getKeyMethodMap()
	 */
	protected Map getKeyMethodMap() {
		Map map = new HashMap();
		map.put("batchProcess.management.display", "displayManagementDashboard");
		map.put("batchProcess.active.display", "displayActiveJobs");
		
		map.put("button.batchProcessManagement.pauseAll", "pauseAll");
		map.put("button.batchProcessManagement.resumeAll", "resumeAll");
		
		map.put("link.batchProcessManagement.execute", "executeBatchProcess");
		
		map.put("link.batchProcessManagement.viewExecuteWithArgs", "viewExecuteWithArgs");		
		map.put("button.batchProcessDetail.executeWithArgs", "executeBatchProcessWithArgs");
		
		map.put("link.batchProcessManagement.viewConfig", "viewConfig");
		map.put("button.batchProcessDetail.updateConfig", "updateConfig");
		
		map.put("link.batchProcessActive.markAsCancelled", "markActiveJobAsCancelled");
		map.put("link.batchProcessActive.markAsError", "markActiveJobAsError");
		
		map.put("link.batchProcessManagement.history", "viewHistory");				
		
		return map;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		Validate.notNull(invoker, "A BatchProcessInvoker must be non-null");
		Validate.notNull(schedulingService, "A schedulingService must be non-null");
		Validate.notNull(batchProcessService, "A batchProcessService must be non-null");
	}

	/**
	 * @return Returns the invoker.
	 */
	public BatchProcessInvoker getInvoker() {
		return invoker;
	}

	/**
	 * @param invoker The invoker to set.
	 */
	public void setInvoker(BatchProcessInvoker invoker) {
		this.invoker = invoker;
	}

	/**
	 * @return Returns the schedulingService.
	 */
	public SchedulingService getSchedulingService() {
		return schedulingService;
	}

	/**
	 * @param schedulingService The schedulingService to set.
	 */
	public void setSchedulingService(SchedulingService schedulingService) {
		this.schedulingService = schedulingService;
	}

	/**
	 * @return Returns the batchProcessService.
	 */
	public BatchProcessService getBatchProcessService() {
		return batchProcessService;
	}

	/**
	 * @param batchProcessService The batchProcessService to set.
	 */
	public void setBatchProcessService(BatchProcessService batchProcessService) {
		this.batchProcessService = batchProcessService;
	}
}