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

import gov.va.med.esr.common.infra.ImpreciseDateUtils;
import gov.va.med.esr.common.model.ee.MilitaryService;
import gov.va.med.esr.common.model.ee.MilitaryServiceEpisode;
import gov.va.med.esr.common.model.ee.MilitaryServiceSiteRecord;
import gov.va.med.esr.common.model.lookup.ServicePeriod;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.PersonMergeInfo;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.PersonIdEntityKeyImpl;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.MilitaryInfoService;
import gov.va.med.esr.service.PersonHelperService;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.service.PersonMergeService;
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.service.ServiceException;
import gov.va.med.fw.service.transaction.TransactionTimestampManager;

import java.math.BigDecimal;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;
/**
 * The batch process is to update service period for Imprecise dates for military 
 * service. Imprecise dates are the start_date and end_date that has missing month
 * and/or day and re-assign them to default month and day for the missing dates to
 * make them as precise dates to calculate the period of service.
 * 
 * RTC Defect # 328325
 * 
 * 
 * @version 1.0
 */
public class BatchUpdateServiceDates extends AbstractDataQueryIncrementalProcess {

    private PersonService personService;
    private LookupService lookupService;
    private PersonHelperService personHelperService;
    private MilitaryInfoService militaryInfoService;
    private TransactionTimestampManager timestampManager;
    
    /**
     * Corrie's Input
     * calculate the period of military service.
        1. The precise MSE date will be set in the background and never displayed in the ES/ADR.
        It will not be saved on the record, dates altered only for POS calculation purposes in the background while it is running. 
        2. The Service entry date with imprecise start date should be set to first of the month, and imprecise end date should be to set to end of that month. 
             • SED on File Jan 2011 set top January 1, 2011
             • SSD on file Jan 2015 set to January 31, 2015
             If the ES has only a year set as follows.
             • SED on File 2011 set top January 1, 2011
             • SSD on file 2015 set to December 31, 2015
            For example:
             Military Service Episodes - HEC
             Service Entry Date=03/1946 
             Service Separation Date=09/1947
             Then treat it as below in memory to calculate POS:
             Military Service Episodes - HEC
             Service Entry Date=03/01/1946 
             Service Separation Date=09/30/1947
     * @param DataQueryProcessExecutionContext context
     * @param List acquiredDatas
     */
    protected void executeProcess(DataProcessExecutionContext context) throws Exception {
    	if(context.getExecutionArguments() != null){
    	this.setParamNames(new String[] {"rowCount"});
        this.setParamValues(new Integer[] {Integer.valueOf((String)context.getExecutionArguments())});
    	}
    	if(context.getExecutionArguments() == null){
    		this.setParamNames(new String[] {"rowCount"});
    		this.setParamValues(new Integer[] {100000}); //default 100K
    	}
        super.executeProcess(context);
		
	}
    public void processData(DataQueryProcessExecutionContext context, List acquiredData) {
        // TODO Auto-generated method stub
        if (logger.isDebugEnabled())
            logger.debug(getProcessName()
                    + "Service Dates batch process: Query increment result Size="
                    + (acquiredData == null ? 0 : acquiredData.size()));

        if (acquiredData == null) {
            return;
        }

        // Add check for isInterrupted so don't have to wait for entire batch to
        // finish
        for (int i = 0; i < acquiredData.size() && !isInterrupted(context); i++) {
            
            Object[] row = (Object[]) acquiredData.get(i);
            BigDecimal personId = (BigDecimal) row[0];
            BigDecimal mseid = (BigDecimal) row[2];
            String serviceEntryDate = (String) row[4];
            String serviceSeparationDate = (String) row[5];
            if(serviceEntryDate != null && serviceSeparationDate!=null){
                if(serviceEntryDate.length()==6 ||serviceEntryDate.length()==4 || serviceSeparationDate.length()==6 ||serviceSeparationDate.length()==4){
                    try {
                        serviceEntryDate=this.getMilitaryService().getPreciseServiceEntryDate(serviceEntryDate);
                        serviceSeparationDate=this.getMilitaryService().getPreciseServiceSeparationDate(serviceSeparationDate);
                    } catch (ServiceException e1) {
                    	logger.error("Failed to get Precise Service Dates: " + personId);
                        context.getProcessStatistics().incrementNumberOfErrorRecords();
                    }
                    PersonEntityKey key = new PersonIdEntityKeyImpl(personId);
                    try {
                        // Get the current person on file
                        Person person  = this.getPersonService().getPerson(key);               
                        MilitaryService militaryService = person.getMilitaryService();
                        MilitaryServiceSiteRecord hecSite = militaryService.getHECMilitaryServiceSiteRecord();
                        Set episodes = hecSite.getMilitaryServiceEpisodes();
                        // Sets the service Entry Date and Separation Dates based on the 
                        // matched onFile mseID with one from the person ID
                        for (Iterator j = episodes.iterator(); j.hasNext();) {
                            MilitaryServiceEpisode hecMilitaryServiceEpisode = (MilitaryServiceEpisode) j.next();
                             if (hecMilitaryServiceEpisode.getEntityKey().getKeyValue().toString().equals(mseid.toString())){ 
                                // setting Start Date and end dates.
                                hecMilitaryServiceEpisode.setStartDate( ImpreciseDateUtils.createImpreciseDate(Integer.parseInt(serviceEntryDate.substring(0,4)), Integer.parseInt(serviceEntryDate.substring(4,6)), Integer.parseInt(serviceEntryDate.substring(6)) ) );
                                hecMilitaryServiceEpisode.setEndDate( ImpreciseDateUtils.createImpreciseDate( Integer.parseInt(serviceSeparationDate.substring(0,4)), Integer.parseInt(serviceSeparationDate.substring(4,6)), Integer.parseInt(serviceSeparationDate.substring(6))));                                    
                             }
                            try {
                            	 if(serviceEntryDate != null && serviceSeparationDate!=null){
                                Person updated = this.getMilitaryService().updateCalculatePeriodOfService( person );
                            	 
                                militaryService = updated.getMilitaryService();
                                hecSite = militaryService.getHECMilitaryServiceSiteRecord();
                                // Get a service period to check if the newly calculated period of service is correct
                                ServicePeriod period = hecSite.getServicePeriod();
                                ServicePeriod servicePeriod = (hecSite != null) ? hecSite.getServicePeriod() : null;
                                if(servicePeriod != null) {
                                    String posCode = servicePeriod.getCode();
                                    context.getProcessStatistics().incrementNumberOfSuccessfulRecords();
                                    logger.info("Recalculated POS is "+posCode+":"+servicePeriod.getName()+servicePeriod.getDescription());
                                }
                                if(servicePeriod == null) {
                                    context.getProcessStatistics().incrementNumberOfErrorRecords();
                                    logger.error("Failed to get servicePeriod: " + personId);
                                }
                             }
                            }
                            catch( Exception e ) {
                            	logger.error("Validtion Error Unable to calculate service period: " + personId);
                                context.getProcessStatistics().incrementNumberOfErrorRecords();
                                if( logger.isDebugEnabled() ) {
                                    logger.debug( "Unable to calculate service period ", e );
                                }
                            }
                        }
                    } catch (ServiceException e) {
                    	logger.error("Person Not Found: " + personId);
                        context.getProcessStatistics().incrementNumberOfErrorRecords();
                    }
                }
            }
            if (logger.isDebugEnabled())
                logger.debug("Processing person Servicedate record # " + (i + 1)
                        + " with primary person id =" + personId);
            // Update statistics more frequently than once per batch (default size 500)
            if (context.getProcessStatistics().isTotalNumberMod(DEFAULT_JOB_RESULT_UPDATE_INTERVAL))
                this.updateJobResult(context);
        }

        if (logger.isDebugEnabled()) {
            logger.debug(getProcessName()
                    + ": Processing of current batch complete.");
            logger.debug(getProcessName()
                    + ": Successful records count = "
                    + context.getProcessStatistics()
                            .getNumberOfSuccessfulRecords());
            logger.debug(getProcessName() + ": Failure records count ="
                    + context.getProcessStatistics().getNumberOfErrorRecords());
        }
        
    }

    public LookupService getLookupService() {
        return lookupService;
    }

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

    public PersonService getPersonService() {
        return personService;
    }

    public void setPersonService(PersonService personService) {
        this.personService = personService;
    }
    
    public PersonHelperService getPersonHelperService() {
        return personHelperService;
    }

    public void setPersonHelperService(PersonHelperService personHelperService) {
        this.personHelperService = personHelperService;
    }
    public TransactionTimestampManager getTimestampManager() {
        return timestampManager;
    }

    public void setTimestampManager(TransactionTimestampManager timestampManager) {
        this.timestampManager = timestampManager;
    }
	/**
	 * @return Returns the militaryService.
	 */
	public MilitaryInfoService getMilitaryService() {
		return this.militaryInfoService;
	}

	/**
	 * @param militaryService The militaryService to set.
	 */
	public void setMilitaryService(MilitaryInfoService militaryService) {
		this.militaryInfoService = militaryService;
	}

    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        Validate.notNull(personService);
        Validate.notNull(lookupService);
        Validate.notNull(timestampManager);
        Validate.notNull(militaryInfoService);
    }
}
