package gov.va.med.esr.common.batchprocess;

import gov.va.med.esr.common.model.comms.HandBookBatchRequest;
import gov.va.med.esr.common.model.lookup.ComLetterTemplateType;
import gov.va.med.esr.common.model.lookup.HandBookRequestStatusType;
import gov.va.med.esr.service.HandBookService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.fw.batchprocess.AbstractDataQueryIncrementalProcess;
import gov.va.med.fw.batchprocess.DataProcessExecutionContext;
import gov.va.med.fw.batchprocess.DataQueryProcessExecutionContext;
import gov.va.med.fw.model.EntityKeyFactory;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.persistent.QueryInfo;
import gov.va.med.fw.persistent.hibernate.AbstractDAOAction;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.util.StopWatchLogger;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.Validate;
import org.hibernate.Query;
import org.hibernate.Session;


/**
 * A class for the process to manage generation of handbooks for publication in support of the 
 * Veterans Health Benefits Communications (handbook) initiative.
 * 
 *
 */
public class HandbookBulkGenerateProcess extends AbstractDataQueryIncrementalProcess {
	private static final String PARAM_BATCHID = "batchreq_id";
    private static final String BATCH_RELEASE_COUNT_QUERY = "handbookBulkGenerateProcess.countPerBatch"; 
    private static final String IS_PERSON_ALIVE_QUERY = "handbookBulkGenerateProcess.isPersonAlive";
	private static final String PARAM_PERSONID = "person_id";
	    
	private HandBookService handbookService = null;
	private LookupService lookupService = null;
	
	private Set batchRequests = new HashSet();
	
	protected void processData(DataQueryProcessExecutionContext context, List acquiredData) {
		if(logger.isInfoEnabled())
			logger.info("Handbook Bulk Generate Process: Query result Size="+(acquiredData == null ? 0 : acquiredData.size()));

		if (acquiredData == null)
			return;

		StopWatchLogger watch = null;

		for (int i = 0; i < acquiredData.size(); i++) {
			
			if (logger.isDebugEnabled()) {
				watch = new StopWatchLogger(ClassUtils.getShortClassName(getClass())
						+ " processData[" + i + "]");
				if (watch != null) {
					watch.start();
				}
			}

			// person Id
			BigDecimal personId = (BigDecimal) ((Object [])acquiredData.get(i))[0];
			// handbook batch request id
			BigDecimal batchRequestId = (BigDecimal) ((Object [])acquiredData.get(i))[1];
			// handbook release control number id
			BigDecimal releaseControlId = (BigDecimal) ((Object [])acquiredData.get(i))[2];
			
			try {
				
				// If person is deceased, do not add to handbook mail queue
				if (isPersonAlive(personId)) {
					getHandbookService().request400HHandBookMail(personId, null, String.valueOf(batchRequestId), releaseControlId);							
				}
                context.getProcessStatistics().incrementNumberOfSuccessfulRecords();                
					
			} catch (Exception ex) {
				context.getProcessStatistics().incrementNumberOfErrorRecords();
				String errMsg = "Error adding entry to HandbookMailQueue for person "
					+ personId;
				context.getExceptionData().add(errMsg+ " Reason: "+ ex.getMessage());
				if(logger.isWarnEnabled())
					logger.warn(errMsg + " Reason: ", ex);
			} finally {
				if (logger.isDebugEnabled() && watch != null) {
					watch.stopAndLog();
				}
			}
		}
		
		if(logger.isDebugEnabled()) {
			logger.debug("Handbook Bulk Generate Process: Processing complete.");
			logger.debug("Handbook Bulk Generate Process: Successful records count = "+context.getProcessStatistics().getNumberOfSuccessfulRecords());
			logger.debug("Handbook Bulk Generate Process: Failure records count ="+context.getProcessStatistics().getNumberOfErrorRecords());
		}
		
	}
	
	private boolean isPersonAlive(BigDecimal personId) throws DAOException {
		StopWatchLogger watch = null;
		
		if (logger.isDebugEnabled()) {
			watch = new StopWatchLogger(ClassUtils.getShortClassName(getClass())
					+ " isPersonAlive(" + personId + ")");
			if (watch != null) {
				watch.start();
			}
		}
		
		// execute query to count handbook mail queue records inserted per batch request
		Map contextData = new HashMap();
        contextData.put("personId", personId);   
        
        // check to see if person is alive
        AbstractDAOAction callback = new AbstractDAOAction(contextData) { 
            public Object execute(Session session) {
                Query query = session.getNamedQuery(IS_PERSON_ALIVE_QUERY);
                query.setParameter(PARAM_PERSONID, (BigDecimal)getContextData().get("personId"));
                return query.list();  
             }
        };

        List result = (List) getDao().execute(callback);
		
		if (logger.isDebugEnabled() && watch != null) {
			watch.stopAndLog();
		}

        return (result != null && !result.isEmpty());
	}
	
	protected void handleDataProcessCompleted(DataProcessExecutionContext context) {	
		// update HandbookBatchRequest
		int count = 0;
		
		for (Iterator iter = this.batchRequests.iterator(); iter.hasNext();) {
			BigDecimal batchRequestId = (BigDecimal) iter.next();
			try {
				count = getCount(batchRequestId);
				updateBatchRequest(batchRequestId, count);
			} catch (Exception ex) {
				String errMsg = "Error getting count and updating batch request for request ID = " + batchRequestId;
				context.getExceptionData().add(errMsg+ " Reason: "+ ex.getMessage());  
				if(logger.isWarnEnabled())
					logger.warn(errMsg + " Reason: ", ex);
			}
		}
		super.handleDataProcessCompleted(context);
	}
	
	/**
	 * Get the handbook_mail_queue record count for a given batch request
	 * @param batchRequestId
	 * @return
	 */
	private int getCount(BigDecimal batchRequestId) throws DAOException {

		// execute query to count handbook mail queue records inserted per batch request
        Map contextData = new HashMap();
        contextData.put("batchreqId", batchRequestId);   
        
       // count records for each batch
        AbstractDAOAction callback = new AbstractDAOAction(contextData) { 
            public Object execute(Session session) {
                Query query = session.getNamedQuery(BATCH_RELEASE_COUNT_QUERY);
                query.setParameter(PARAM_BATCHID, (BigDecimal)getContextData().get("batchreqId"));
                return query.uniqueResult();  
             }
        };

        Integer count = (Integer) getDao().execute(callback);
		
        return count == null ? 0 : count.intValue();
	}
	
	private void updateBatchRequest(BigDecimal batchRequestId, int batchSize) throws ServiceException {

		HandBookBatchRequest batchRequest = this.handbookService.getHandbookBatchRequestEntry(EntityKeyFactory.createEntityKey(batchRequestId, HandBookBatchRequest.class));
		
		if (batchRequest != null) {
			// update the handbook request release size
			batchRequest.setBatchReleaseSize(Integer.toString(batchSize));
			// update the status to completed
			HandBookRequestStatusType completedStatus = this.getLookupService().getHandBookRequestStatusTypeByCode(HandBookRequestStatusType.COMPLETED.getCode());
			batchRequest.setHandBookRequestStatus(completedStatus);
			// save handbook request
			this.handbookService.saveHandBookBatchRequest(batchRequest);
		}
		else throw new ServiceException("HandbookBatchRequest not found for id = " + batchRequestId);
	}
	
	protected List doAcquireData(DataQueryProcessExecutionContext context) throws Exception {
		// add performance logging
		StopWatchLogger watch = null;
		if (logger.isDebugEnabled()) {
			watch = new StopWatchLogger(ClassUtils.getShortClassName(getClass())
					+ " doAcquireData");
			if (watch != null) {
				watch.start();
			}
		}
		
		List acquiredData = null;
		
		try {
			// first retrieve ids to all the 'NEW' batch requests and store them (so that we can
			// update their status later
			batchRequests = new HashSet(this.handbookService.getNewHandBookBatchRequestIds());
		    acquiredData = super.doAcquireData(context);
		} catch (Exception e) {
			throw(e);
		} finally {
			if (logger.isDebugEnabled() && watch != null)
				watch.stopAndLog();
		}
		return acquiredData;
	}
	
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
		Validate.notNull(handbookService,	"A HandBookService is needed");
		Validate.notNull(lookupService,	"A LookupService is needed");
	}

	public HandBookService getHandbookService() {
		return handbookService;
	}

	public void setHandbookService(HandBookService handbookService) {
		this.handbookService = handbookService;
	}

	public LookupService getLookupService() {
		return lookupService;
	}

	public void setLookupService(LookupService lookupService) {
		this.lookupService = lookupService;
	}	
}
