/*******************************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ******************************************************************************/
package gov.va.med.esr.service.impl;

import gov.va.med.esr.common.clock.Clock;
import gov.va.med.esr.common.clock.Clock.Type;
import gov.va.med.esr.common.model.cases.Assignment;
import gov.va.med.esr.common.model.cases.Status;
import gov.va.med.esr.common.model.cases.WorkflowCase;
import gov.va.med.esr.common.model.cases.WorkflowPerson;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;
import gov.va.med.esr.common.model.lookup.FunctionalGroup;
import gov.va.med.esr.common.model.lookup.NameType;
import gov.va.med.esr.common.model.lookup.MilitaryServiceQueryStatus;
import gov.va.med.esr.common.model.lookup.SSNType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.lookup.WkfCaseStatusType;
import gov.va.med.esr.common.model.lookup.WkfCaseType;
import gov.va.med.esr.common.model.lookup.WkfIssueType;
import gov.va.med.esr.common.model.party.Email;
import gov.va.med.esr.common.model.person.Name;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.SSN;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.model.person.id.VPIDEntityKey;
import gov.va.med.esr.common.model.security.ESRUserPrincipalImpl;
import gov.va.med.esr.common.model.workload.WorkflowCaseInfo;
import gov.va.med.esr.common.persistent.person.PersonDAO;
import gov.va.med.esr.common.persistent.workflow.WorkflowDAO;
import gov.va.med.esr.service.CommsEmailBulletinService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.WorkItemsSummary;
import gov.va.med.esr.service.WorkflowSearchCriteria;
import gov.va.med.esr.service.WorkflowSearchResultBean;
import gov.va.med.esr.service.WorkflowService;
import gov.va.med.esr.service.trigger.BulletinTrigger;
import gov.va.med.fw.model.EntityKey;
import gov.va.med.fw.model.EntityKeyFactory;
import gov.va.med.fw.persistent.DAOException;
import gov.va.med.fw.security.SecurityContextHelper;
import gov.va.med.fw.security.UserPrincipal;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.pagination.SearchQueryInfo;
import gov.va.med.fw.util.DateUtils;
import gov.va.med.fw.util.StringUtils;

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

import org.apache.commons.lang.Validate;

//import bsh.This;

public class WorkflowServiceImpl extends AbstractRuleAwareServiceImpl implements WorkflowService {

	/**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = -6095296322006464695L;

	private WorkflowDAO workflowDAO;

	private PersonDAO personDAO;

	//RTC Task 236654 - Pending Clock/status for VOA
	private static final String VOA_EVENT = "VOA event";



	/**
	 * Get EE work flow cases - this method is implemeneted for PH Cases
	 */
    @SuppressWarnings("rawtypes")
	public WorkflowCase getWorkItem(Person person, WkfCaseType wkfCaseType, boolean create)
        throws ServiceException {

        Validate.notNull(person, "Person is required to create workflow case");
        Validate.notNull(wkfCaseType, "WorkflowCaseType is required to create workflow case");

        WorkflowCase wkfCase = null;
        WorkflowPerson workflowPerson = null; //TODO change to the new object type

        //create search criteria and find the workflow cases
        WorkflowSearchCriteria workflowSeachCriteria = new WorkflowSearchCriteria();
        workflowSeachCriteria.setPersonId(person.getEntityKey().getKeyValueAsString());
        workflowSeachCriteria.setItemType(wkfCaseType);
        workflowSeachCriteria.setOpenItems(true);

        try {

            //Get work items (search results beans)
            List workItems = getWorkflowDAO().find(workflowSeachCriteria);

            if (workItems != null && workItems.size() > 0) {
                if (workItems.size() == 1) {
                    WorkflowSearchResultBean bean = (WorkflowSearchResultBean) workItems.get(0);
                    EntityKey entityKey = EntityKeyFactory.createEntityKey(bean.getWorkflowCaseID(),
                            WorkflowCase.class);
                    wkfCase = (WorkflowCase) getWorkflowDAO().getByKey(entityKey);
               }
                else if (workItems.size() > 1) {
                    throw new ServiceException("Multiple Workflow Items found");
                }
            }

            if (wkfCase == null && create) {
                //Get workflow Person
                EntityKey entityKey = EntityKeyFactory.createEntityKey(
                    new BigDecimal(person.getEntityKey().getKeyValueAsString()), WorkflowPerson.class);

                workflowPerson = (WorkflowPerson) getWorkflowDAO().getByKey(entityKey);

                wkfCase = new WorkflowCase();
                wkfCase.setPerson(workflowPerson);

                FunctionalGroup groupType =
                	getLookupService().getFunctionalGroupByCode(FunctionalGroup.EE.getCode());
                wkfCase.setGroupType(groupType);

            	wkfCase.setRequestSource(getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode()));

                WkfCaseType caseType =
                    getLookupService().getWkfCaseTypeByCode(wkfCaseType.getCode());
                wkfCase.setCaseType(caseType);

                Status wkfStatus = new Status();
                WkfCaseStatusType wkfCaseStatusType =
                    getLookupService().getWkfCaseStatusTypeByCode(WkfCaseStatusType.NEW.getName());
                wkfStatus.setStatusValue(wkfCaseStatusType);
                wkfCase.setStatus(wkfStatus);

                Assignment assignment = new Assignment();
                wkfCase.setAssignment(assignment);
            }

        } catch (DAOException e){
            throw new ServiceException("WorkItem search failed");
        }

        return wkfCase;
    }

    @SuppressWarnings({ "rawtypes", "unchecked", "unused" })
	public List<WorkflowCase> getOpenWorkItemLst(Person person, WkfCaseType wkfCaseType) throws ServiceException {

    Validate.notNull(person, "Person is required to create workflow case");
    Validate.notNull(wkfCaseType, "WorkflowCaseType is required to create workflow case");


    ArrayList lst = new ArrayList();

    WorkflowCase wkfCase = null;
    WorkflowPerson workflowPerson = null; //TODO change to the new object type

    //create search criteria and find the workflow cases
    WorkflowSearchCriteria workflowSeachCriteria = new WorkflowSearchCriteria();
    workflowSeachCriteria.setPersonId(person.getEntityKey().getKeyValueAsString());
    workflowSeachCriteria.setItemType(wkfCaseType);
    workflowSeachCriteria.setOpenItems(true);

    try {

        //Get work items (search results beans)
        List workItems = getWorkflowDAO().find(workflowSeachCriteria);

        if (workItems != null && workItems.size() > 0) {
            for (int i=0; i< workItems.size(); i++) {
                WorkflowSearchResultBean bean = (WorkflowSearchResultBean) workItems.get(i);
                EntityKey entityKey = EntityKeyFactory.createEntityKey(bean.getWorkflowCaseID(), WorkflowCase.class);
                lst.add((WorkflowCase) getWorkflowDAO().getByKey(entityKey));
           }
        }
    } catch (DAOException e){
        throw new ServiceException("WorkItem search failed");
    }

    return lst;
}

    public WorkflowCase getMostRecentWorkflowItem(Person person) throws ServiceException {

		Validate.notNull(person, "Person is required to create workflow case");
		WorkflowCase wkfCase = null;

		try {
			// Get work items (search results beans)
			wkfCase = getWorkflowDAO().getMostRecentlyCreatedWorkflowItem(person.getPersonEntityKey());

		} catch (DAOException e) {
			throw new ServiceException("WorkItem search failed");
		}

		return wkfCase;
	}

    /**
     * Create case
     */
	@SuppressWarnings("rawtypes")
	public WorkflowCase create(WorkflowCase workflowCase)
			throws ServiceException {
		try
        {
            // Enhanced to search as per CCR_9809
            List workflowCases = getWorkflowDAO().getMatchingOpenCasesForAWorkItem(workflowCase);
            if(workflowCases != null && workflowCases.size() > 0 ){
                Iterator wfCaseItr = workflowCases.iterator();
                while(wfCaseItr.hasNext()){
                    WorkflowCase wfCaseFromDB = (WorkflowCase)wfCaseItr.next();
                    if(WkfCaseStatusType.CLOSED.getName().equals(wfCaseFromDB.getStatus())){
                        continue;
                    }
                    else{
                        close(wfCaseFromDB);
                    }
                }

            }
            // create case
			updateStatus(workflowCase);
            getWorkflowDAO().update(workflowCase);
            return workflowCase;
		}
		catch (DAOException e) {
			throw new ServiceException("Workflow Case creation failed", e);
		}
	}

    /**
     * Create workload case for Manage usecases
     * @param person
     * @param groupType
     * @param wkfCaseType
     * @param wkfIssueType
     * @param errorMessage
     * @throws ServiceException
     */
    public void autoCreateCase(Person person, FunctionalGroup groupType,
    		WkfCaseType wkfCaseType, WkfIssueType wkfIssueType, String errorMessage) throws ServiceException
    {
    	Validate.notNull(person,"Person can not null");
    	Validate.notNull(groupType,"GroupType can not null");
    	Validate.notNull(wkfCaseType,"Workload Case Type can not be null");

        //create a new workflow case with Messaging information
        EntityKey entityKey = EntityKeyFactory.createEntityKey(
            new BigDecimal(person.getEntityKey().getKeyValueAsString()),
            WorkflowPerson.class);
        WorkflowPerson workflowPerson = null;
        try {
            workflowPerson = (WorkflowPerson) getWorkflowDAO().getByKey(entityKey);
        }catch (DAOException e) {
            throw new ServiceException("Could not retrive Person for key " +
            		person.getEntityKey().getKeyValueAsString());
        }

        WorkflowCase wkfCase = new WorkflowCase();
        wkfCase.setPerson(workflowPerson);
        wkfCase.setGroupType(groupType);
        wkfCase.setCaseType(wkfCaseType);
        wkfCase.setIssueType(wkfIssueType);
        wkfCase.setErrorMessage(errorMessage);

    	wkfCase.setRequestSource(getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode()));

        //Set status to new
        Status wkfStatus = new Status();
        WkfCaseStatusType wkfCaseStatusType =
            getLookupService().getWkfCaseStatusTypeByCode(WkfCaseStatusType.NEW.getName());
        wkfStatus.setStatusValue(wkfCaseStatusType);
        wkfCase.setStatus(wkfStatus);

        //Set assignment to unassigned
        Assignment assignment = new Assignment();
        wkfCase.setAssignment(assignment);

        //Create case
        create(wkfCase);
    }

    public void autoCreateCase(WorkflowCaseInfo workflowCaseInfo) throws ServiceException
    {
    	//Create case object from data
    	WorkflowCase wkfCase = createCaseFromInfo(workflowCaseInfo);
    	//Create case
        create(wkfCase);
    }

    private WorkflowCase createCaseFromInfo(WorkflowCaseInfo workflowCaseInfo) throws ServiceException
    {
        //create a new workflow case with Messaging information
        EntityKey entityKey = EntityKeyFactory.createEntityKey(
            new BigDecimal(workflowCaseInfo.getPersonEntityKey().getKeyValueAsString()),
            WorkflowPerson.class);
        WorkflowPerson workflowPerson = null;
        try {
            workflowPerson = (WorkflowPerson) getWorkflowDAO().getByKey(entityKey);
        }catch (DAOException e) {
            throw new ServiceException("Could not retrive Person for key " +
            		workflowCaseInfo.getPersonEntityKey().getKeyValueAsString());
        }

        WorkflowCase wkfCase = new WorkflowCase();
        wkfCase.setPerson(workflowPerson);

        FunctionalGroup groupType = workflowCaseInfo.getGroupType();

        //Defaults to an EE Case
        if (groupType == null){
        	groupType = getLookupService().getFunctionalGroupByCode(
        			FunctionalGroup.DQ.getName());
        }

        wkfCase.setGroupType(groupType);

        WkfCaseType caseType = workflowCaseInfo.getCaseType();
        //If case type is not specified create an AE error case
        if (caseType == null) {
            caseType = getLookupService().getWkfCaseTypeByCode(
                WkfCaseType.CODE_APPLICATION_EXCEPTION.getName());
        }

        wkfCase.setCaseType(caseType);

        //Set Issue Type
        WkfIssueType issueType = workflowCaseInfo.getIssueType();
        wkfCase.setIssueType(issueType);

        //Set status to new
        Status wkfStatus = new Status();
        WkfCaseStatusType wkfCaseStatusType =
            getLookupService().getWkfCaseStatusTypeByCode(WkfCaseStatusType.NEW.getName());
        wkfStatus.setStatusValue(wkfCaseStatusType);
        wkfCase.setStatus(wkfStatus);

        //Set assignment to unassigned
        Assignment assignment = new Assignment();
        wkfCase.setAssignment(assignment);
        wkfCase.setErrorMessage(workflowCaseInfo.getErrorMessage());

        //Set the default site to HEC
        if (workflowCaseInfo.getRequestSource() == null) {
        	wkfCase.setRequestSource(getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode()));
        }else {
        	wkfCase.setRequestSource(workflowCaseInfo.getRequestSource());
        }

        return wkfCase;
    }

    public WorkflowCase updateWorkItem(WorkflowCase workflowCase) throws ServiceException {

        // Get the on-file info
        WorkflowCase onFileWorkflowCase = find(workflowCase.getEntityKey());

        // Ensure that something changed on the incoming entity as compared to the onFile entity
        ensureEntityChanged(workflowCase, onFileWorkflowCase);

        // Evict the on-file entity since we aren't overlaying data onto it
        try {
            getWorkflowDAO().evict(onFileWorkflowCase);
        }catch (DAOException e){
            throw new ServiceException("Workflow Case update failed", e);
        }

        //if the case is not assigned assign to the current logged in user
        UserPrincipal currentUser = SecurityContextHelper.getCurrentUser();
        String userName = currentUser == null ? null : currentUser.getName();

        Assignment assignment = getAssignment(workflowCase);
        WkfCaseStatusType onfileStatus = onFileWorkflowCase.getStatus().getStatusValue();
        WkfCaseStatusType currentStatus = workflowCase.getStatus().getStatusValue();

        //New case status is updated - assign to the current user
        if (StringUtils.isEmpty(assignment.getAssignedTo()) &&
                !currentStatus.getCode().equals(onfileStatus.getCode())){
            assignment.setAssignedBy(userName);
            assignment.setAssignedTo(userName);
            assignment.setAssignmentDate(DateUtils.getCurrentDate());
        }

        return update(workflowCase);

    }
	public WorkflowCase update(WorkflowCase workflowCase)
			throws ServiceException {
		try
        {
            // Perform the update
			updateStatus(workflowCase);
            getWorkflowDAO().update(workflowCase);

            return workflowCase;
		}
		catch (DAOException e) {
			throw new ServiceException("Workflow Case update failed", e);
		}
	}

	/**
	 * Close the list of cases
	 */
	@SuppressWarnings("rawtypes")
	public void close(List caseIds) throws ServiceException {

		// Find the list of cases - do we need any bulk operation ?
		WkfCaseStatusType closedStatus = getLookupService()
				.getWkfCaseStatusTypeByCode(WkfCaseStatusType.CLOSED.getName());

		//get currenntly logged in user name
		UserPrincipal currentUser = SecurityContextHelper.getCurrentUser();
		String userName = currentUser == null ? null : currentUser.getName();
		List cases = find(caseIds);

		// update case status
		for (Iterator i = cases.iterator(); i.hasNext();) {
			WorkflowCase wfCase = (WorkflowCase) i.next();
			//Ignore closed cases
			if (wfCase.isOpen()) {
	            Status newStatus = wfCase.getStatus();
				if (newStatus == null) {
	                newStatus = new Status();
	                wfCase.setStatus(newStatus);
	            }
				newStatus.setStatusValue(closedStatus);

				//If the case is not assigned to the current user assign it
				Assignment assignment = wfCase.getAssignment();
				if (assignment == null) {
					assignment = new Assignment();
					wfCase.setAssignment(assignment);
				}

				//New Case not assigned, assign to the current user
				if (userName != null) {
					if (StringUtils.isEmpty(assignment.getAssignedTo())){
						assignment.setAssignedBy(userName);
						assignment.setAssignedTo(userName);
						assignment.setAssignmentDate(DateUtils.getCurrentDate());
					}
				}

				update(wfCase);
			}
		}
	}

    /**
     * Close case
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public void close(WorkflowCase wfCase) throws ServiceException {
        List caseIds = new ArrayList ();
        caseIds.add((BigDecimal)wfCase.getEntityKey().getKeyValue());
        close(caseIds);
    }

    @SuppressWarnings("rawtypes")
	public void assign(List caseIds, UserPrincipal assignedBy,
            UserPrincipal assinedTo) throws ServiceException {
        assign(caseIds, assignedBy, assinedTo, null);
    }

    @SuppressWarnings("rawtypes")
	public void assign(List caseIds, UserPrincipal assignedBy, UserPrincipal assinedTo,
            FunctionalGroup toGroup) throws ServiceException
    {
		List cases = find(caseIds);

		for (Iterator i = cases.iterator(); i.hasNext();) {
			WorkflowCase wfCase = (WorkflowCase) i.next();
			//Ignore closed cases
			if (wfCase.isOpen()) {
				Assignment currentAssignment = wfCase.getAssignment();
				if (currentAssignment == null) {
					currentAssignment = new Assignment();
                    wfCase.setAssignment(currentAssignment);
				}

                //Ignore if the assignment has not changed
                if (assinedTo != null && assinedTo.getName().equals(currentAssignment.getAssignedTo())){
                    continue;
                }

				currentAssignment.setAssignedBy(assignedBy.getName());
				currentAssignment.setAssignedTo(assinedTo == null ? null : assinedTo.getName());
				currentAssignment.setAssignmentDate(DateUtils.getCurrentDate());

				//update current status to assigned if the status is new or Inprocess
				Status status = wfCase.getStatus();
				WkfCaseStatusType wkfStatusType = status.getStatusValue();
                if (assinedTo != null) {
    				if (WkfCaseStatusType.NEW.getCode().equals(wkfStatusType.getCode()) ||
    				    WkfCaseStatusType.INPROCESS.getCode().equals(wkfStatusType.getCode()))
    				{
    					wkfStatusType = getLookupService().getWkfCaseStatusTypeByCode(WkfCaseStatusType.ASSIGNED.getCode());
    					status.setStatusValue(wkfStatusType);
    				}
                }
                //When the case is assigned to unassigned pool set the status to NEW
                else {
                    if (!WkfCaseStatusType.NEW.getCode().equals(wkfStatusType.getCode()))
                    {
                        wkfStatusType = getLookupService().getWkfCaseStatusTypeByCode(WkfCaseStatusType.NEW.getCode());
                        status.setStatusValue(wkfStatusType);
                    }
                }

                //If the functional group is specified and is different update
                if (toGroup != null && !toGroup.equals(wfCase.getGroupType())) {
                    wfCase.setGroupType(toGroup);
                }
				update(wfCase);
			}
		}
	}

	/**
	 * Common search method for retrieving the case list
	 */
	@SuppressWarnings("rawtypes")
	public List search(SearchQueryInfo searchQueryInfo) throws ServiceException {
			return searchwithPSIM(searchQueryInfo);
	}

	/**
	 * Search workflow cases by using psdelegate service
	 * @param searchQueryInfo
	 * @return
	 * @throws ServiceException
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List searchwithPSIM(SearchQueryInfo searchQueryInfo) throws ServiceException {

		List workItems = null;
		try {
			WorkflowSearchCriteria searchCriteria = (WorkflowSearchCriteria) searchQueryInfo;
			List vpids = null;
			Map identiyTraitsByVPID = null;

			//check whether we have any PSIM criteria specified
			//All PSIM searches require ssn or First Name and Last Name
			//and expecting a single identity traits
			if (StringUtils.isNotEmpty(searchCriteria.getSsn()) ||
					(StringUtils.isNotEmpty(searchCriteria.getFamilyName()) &&
					StringUtils.isNotEmpty(searchCriteria.getGivenName()))){

				identiyTraitsByVPID = searchPSIM(searchCriteria);

				if (identiyTraitsByVPID != null && identiyTraitsByVPID.size() > 0) {
					vpids = new ArrayList(identiyTraitsByVPID.keySet());
				}
				else {
					//set default values on the search query info
					searchQueryInfo.setTotalNumberOfEntries(0);
					searchQueryInfo.setSearchTypePerformed(SearchQueryInfo.SEARCH_READ_ALL);
					return new ArrayList(); //Empty Result set
				}
			}

			// update search criteria to include VPIDS
			if (vpids != null && vpids.size() > 0)
			{
				searchCriteria.setVpids(vpids);
				searchCriteria.setPersonIds(new ArrayList(getPersonDAO().getIdsByVPIDs(vpids).values()));
			}

			// Workflow Search Results
            workItems = getWorkflowDAO().find(searchCriteria);

            // now loop through the workItems and set the vpids.
            List personIds = new ArrayList();
            for ( int i=0; workItems!= null && i< workItems.size(); i++)
            {
            	WorkflowSearchResultBean wkfBean = (WorkflowSearchResultBean) workItems.get(i);
            	personIds.add(wkfBean.getPersonId());
            }

            Map vpidMap = null;

            if ( personIds != null && personIds.size() > 0 )
            {
            	vpidMap = personDAO.getVPIDsbyIds(personIds);
            }

            if (vpidMap != null) {
	            for ( int i=0; workItems!= null && i< workItems.size(); i++)
	            {
	            	WorkflowSearchResultBean wkfBean = (WorkflowSearchResultBean) workItems.get(i);
	            	BigDecimal personId = wkfBean.getPersonId();
	            	String vpid = (String) vpidMap.get(personId);

	            	if ( vpid != null )
	            	{
	            		String shortVpid =  vpid.substring(6,23);
	            		wkfBean.setShortVPID(shortVpid);
	            	}
	            }
            }
			//DNS   doank CCR8600 The total is already set in the PaginatedService
            //Comment out because this is very specific to In Memory Search
            //searchQueryInfo.setTotalNumberOfEntries(workItems.size());
		}
		catch (DAOException e) {
			throw new ServiceException("Workflow Search Failed", e);
		}
		return workItems;
	}

	/**
	 * Find Case by Id
	 */
	public WorkflowCase find(EntityKey entityKey) throws ServiceException {
		try {
			WorkflowCase wfCase = (WorkflowCase) getWorkflowDAO().getByKey(
					entityKey);
			if (wfCase != null) {
				// get Person details from PSIM
				VPIDEntityKey vpidEntityKey = wfCase.getVPIDEntityKey();
				PersonIdentityTraits identiyTraits = getPsDelegateService()
						.getIdentityTraits(vpidEntityKey);
				// Merge Person identity Traits information
				mergeIdentityTraitInfo(wfCase, identiyTraits);
			}
			return wfCase;
		}
		catch (DAOException e) {
			throw new ServiceException("WorkflowCase retrival Failed ", e);
		}
	}

	/**
	 * Find cases by ids
	 * optimize to get the cases in bulk query
	 * @param caseIds
	 * @return
	 * @throws ServiceException
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List find(List caseIds) throws ServiceException {
		List cases = new ArrayList();
		try {
			for (Iterator i = caseIds.iterator(); i.hasNext();) {
                BigDecimal caseId = (BigDecimal) i.next();
				WorkflowCase wfCase = (WorkflowCase) getWorkflowDAO().getByKey(
						EntityKeyFactory.createEntityKey(caseId, WorkflowCase.class));
				if (wfCase != null) {
					cases.add(wfCase);
				}
			}
		}
		catch (DAOException e) {
			throw new ServiceException("WorkflowCase retrival Failed ", e);
		}

		return cases;
	}

    public int getOpenCasesCount(PersonEntityKey personEntityKey) throws ServiceException {
        Validate.notNull(personEntityKey, "Person Entity Key is required to get the count");
        try {
            return getWorkflowDAO().getOpenCasesCount(personEntityKey);
        }catch (DAOException e) {
            throw new ServiceException("Get Open Cases Count for Veteran Failed",e);
        }
    }


    @SuppressWarnings({ "rawtypes", "unchecked" })
	public void processWorkflowEvent(Person person, Type clock) throws ServiceException {
    	Validate.notNull(person, "A veteran must not be null");
        Person onFile = this.getPersonService().getPerson((PersonEntityKey)person.getEntityKey());
    	if (clock != null && Clock.Type.WF_72_HOUR_CLOCK.getName().equals(clock.getName())){

    	  if(onFile.getMilitaryService() != null && onFile.getMilitaryService().getMilitaryServiceQueryStatus() != null) {

    		MilitaryServiceQueryStatus status = onFile.getMilitaryService().getMilitaryServiceQueryStatus();
    		if (MilitaryServiceQueryStatus.QUERIED_PENDING_RESPONSE.getCode().equals(status.getCode())) {


    			WkfCaseType type = this.getLookupService().getWkfCaseTypeByCode(WkfCaseType.CODE_MILITARY_SERVICE.getCode());
    			FunctionalGroup group = this.getLookupService().getFunctionalGroupByCode(FunctionalGroup.EE.getCode());
    			WorkflowCaseInfo info = new WorkflowCaseInfo();
    	        info.setPersonEntityKey(onFile.getPersonEntityKey());
    	        info.setErrorMessage("Military Service Query Pending - No Response within 3 Days.");
    	        info.setGroupType(group);
    	        info.setCaseType(type);
    			this.autoCreateCase(info);

    			status = this.getLookupService().getMilitaryServiceQueryStatusByCode(MilitaryServiceQueryStatus.QUERIED_NO_DATA_RECEIVED.getCode());
    			onFile.getMilitaryService().setMilitaryServiceQueryStatus(status);
    			this.getPersonService().save(onFile);
    		}
    	   }

			//RTC Task 236654 - Pending Clock/status for VOA
    		//Create an workitem when the clock is past 72 hours

    			boolean isEnrollStatusPending = false;
    			boolean isTriggeredByVOA = false;

    			if (onFile.getEnrollmentDetermination() != null && onFile.getEnrollmentDetermination().getEnrollmentStatus() != null) {

	    			String statusCd = onFile.getEnrollmentDetermination().getEnrollmentStatus().getCode();
	    			logger.info("status code for VOA wf item is :" + statusCd);

	    			isEnrollStatusPending = EnrollmentStatus.CODE_PENDING.getCode().equals(statusCd)
	    					|| EnrollmentStatus.CODE_PENDING_ELIGIBILITY_STATUS_IS_UNVERIFIED.getCode().equals(statusCd)
	    					|| EnrollmentStatus.CODE_PENDING_MEANS_TEST_REQUIRED.getCode().equals(statusCd)
	    					|| EnrollmentStatus.CODE_PENDING_NO_ELIGIBILITY_CODE_IN_VIVA.getCode().equals(statusCd)
	    					|| EnrollmentStatus.CODE_PENDING_OTHER.getCode().equals(statusCd)
	    					|| EnrollmentStatus.CODE_PENDING_PURPLE_HEART_UNCONFIRMED.getCode().equals(statusCd);

	    			logger.info("Is VOA still in pending state? :" + isEnrollStatusPending);

	    			if (onFile.getEnrollmentDetermination().getCreatedBy().getName().equalsIgnoreCase(VOA_EVENT) ||
	    					onFile.getEnrollmentDetermination().getModifiedBy().getName().equalsIgnoreCase(VOA_EVENT)) {
	    				isTriggeredByVOA = true;
	    			}

	    			/**
	    	         * RTC # 372038 - If record exist in the ES with a Non Veteran Primary Eligibility Code
	    	         * then system should treat as a new applicant for VA health care and make queries to VBA and MSDS
	    	         * (SDM Tkt # I9613978FY16)
	    	         *
	    	         * If no information returned to verify eligibility from MSDS and VBA
	    	         * then leave Primary Eligibility Code as the Non-Veteran Code
	    	         * and create a work item indicating the Veteran is in a Pending Status.
	    	         */
	    			//boolean isNonVeteran_PrimaryEligibilityType = this.getHelperService().isNonVeteranPrimaryEligibility(onFile);
	    			//boolean isVeteran = onFile.isVeteran().booleanValue();
	    			//boolean isNoInfoReturnedToVerifyEligibility = (!isVeteran && isNonVeteran_PrimaryEligibilityType);

	    			if (isEnrollStatusPending && isTriggeredByVOA){
	    					this.autoCreateCase(person, getLookupService().getFunctionalGroupByCode(FunctionalGroup.EE.getCode()),
	    							getLookupService().getWkfCaseTypeByCode(WkfCaseType.CODE_VETERANS_ONLINE_APPLICATION.getCode()), null,
	    							"VOA Submission with Determined Enrollment Status of PENDING");

	    					this.getPersonService().save(onFile);

	    					if (onFile.getEmails() != null && onFile.getEmails().size() > 0) {
	    						final String toList[] = new String[onFile.getEmails().size()];
	    						int i = 0;
	    						for (Iterator it = onFile.getEmails().iterator(); it.hasNext();) {
	    							toList[i] = ((Email) it.next()).getAddress();
	    							i++;
	    						}
	    						try {
		    						if (this.getBulletinService() != null) {
		    							final CommsEmailBulletinService emailSrv = this.getBulletinService();
		    							final Map bulletinData = new Hashtable();
		    							bulletinData.put("FirstName", onFile.getLegalName().getGivenName());
		    							bulletinData.put("LastName", onFile.getLegalName().getFamilyName());

		    							emailSrv.sendEmailBulletinTo(BulletinTrigger.DataType.VOA_NOTIFY_VET_NEED_MORE_INFO, bulletinData, toList);

		    						}
    							}  catch (Throwable e) {
    								logger.error("VOA need more info email failed " + e.getMessage());
    							}
	    					}
	    			}
    			} else {
    				this.autoCreateCase(person, getLookupService().getFunctionalGroupByCode(FunctionalGroup.EE.getCode()),
							getLookupService().getWkfCaseTypeByCode(WkfCaseType.CODE_VETERANS_ONLINE_APPLICATION.getCode()), null,
							"VOA Submission with No Enrollment Determination");
					this.getPersonService().save(onFile);
    			}


    	  }
	}

	public WorkItemsSummary getWorkItemsSummaryForUser(UserPrincipal user) throws ServiceException
    {
        WorkItemsSummary workItemsSummary = new WorkItemsSummary();
        String userName = user.getName();
        FunctionalGroup fg = ((ESRUserPrincipalImpl)user).getFunctionalGroup();
        try {
            //Get summary count for cases assigned to the user
             WorkflowSearchCriteria workflowSearchCriteria = new WorkflowSearchCriteria();

            //My Cases count of all the cases assigned to the user
            workflowSearchCriteria.setAssignedTo(userName);
            workflowSearchCriteria.setOpenItems(true);

            workItemsSummary.setMyCaseCount(getWorkflowDAO().getWorkItemsCount(
                workflowSearchCriteria));

            //My Overdue cases
            workflowSearchCriteria.setOverdueItems(true);
            workItemsSummary.setMyOverdueCaseCount(getWorkflowDAO().getWorkItemsCount(
                workflowSearchCriteria));

            if (fg != null)
                workflowSearchCriteria.setFunctionalGroup(fg);

            //Assigned Cases
            workflowSearchCriteria.setAssignedTo(null);
            workflowSearchCriteria.setOverdueItems(false);
            workflowSearchCriteria.setAssignedItems(true);
            workItemsSummary.setAssignedCaseCount(getWorkflowDAO().getWorkItemsCount(
                    workflowSearchCriteria));
            //Overdue Assigned Cases
            workflowSearchCriteria.setOverdueItems(true);
            workItemsSummary.setAssignedOverdueCaseCount(getWorkflowDAO().getWorkItemsCount(
                    workflowSearchCriteria));

            //UnAssigned Cases
            workflowSearchCriteria.setOverdueItems(false);
            workflowSearchCriteria.setAssignedItems(false);
            workflowSearchCriteria.setUnassignedItems(true);
            workItemsSummary.setUnassignedCaseCount(getWorkflowDAO().getWorkItemsCount(
                    workflowSearchCriteria));
            //Overdue UnAssigned Cases
            workflowSearchCriteria.setOverdueItems(true);
            workItemsSummary.setUnassignedOverdueCaseCount(getWorkflowDAO().getWorkItemsCount(
                    workflowSearchCriteria));

            return workItemsSummary;

        } catch (Exception e){
            throw new ServiceException("Summary count query failed",e);
        }
    }
	/**
	 * Merge Identity traits
	 *
	 * @param workflowCase
	 * @param identityTraits
	 */
	private void mergeIdentityTraitInfo(WorkflowCase workflowCase,
			PersonIdentityTraits identityTraits) {
		if (identityTraits != null) {
			WorkflowPerson person = workflowCase.getPerson();
			person.setPersonTraits(identityTraits);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Map searchPSIM(WorkflowSearchCriteria searchCriteria)
			throws ServiceException {
		// Check whether we have SSN/FN/LS specified
		String ssn = searchCriteria.getSsn();
		String firstName = searchCriteria.getGivenName();
		String lastName = searchCriteria.getFamilyName();
		Map traitsKeyedByVPID = new HashMap();

		// If atleast one of the attributes of Identity Traits is specified
		if (StringUtils.isNotEmpty(ssn) || StringUtils.isNotEmpty(firstName)
				|| StringUtils.isNotEmpty(lastName)) {

			PersonIdentityTraits searchCriteriaForTraits = new PersonIdentityTraits();

			// populate name
			if (StringUtils.isNotEmpty(firstName)
					|| StringUtils.isNotEmpty(lastName)) {
				Name legalName = new Name();
				legalName.setFamilyName(StringUtils.trimToNull(searchCriteria
						.getFamilyName()));
				legalName.setGivenName(StringUtils.trimToNull(searchCriteria
						.getGivenName()));
				// legalName.setMiddleName(null);
				legalName.setType(getLookupService()
						.getNameTypeByCode(NameType.LEGAL_NAME.getName()));
				searchCriteriaForTraits.addName(legalName);
			}

			// populate ssn ssn is always required
			if (StringUtils.isNotEmpty(ssn)) {
				SSN ssnObj = new SSN();
				ssnObj.setType(getLookupService().getSSNTypeByCode(
						SSNType.CODE_ACTIVE.getName()));
				ssnObj.setSsnText(StringUtils.trimToNull(SSN
						.formatSSN(searchCriteria.getSsn())));
				searchCriteriaForTraits.setSsn(ssnObj);
			}

			// execute search
			 //CR 11776- Replacing EJB calls, added composite search option
			Set matchingTraits = getPsDelegateService().attendedSearch(searchCriteriaForTraits, true); //CCR 11776- non-composite
			Iterator itr = matchingTraits != null ? matchingTraits.iterator(): null;

			// build list of matches from PS
			PersonIdentityTraits match = null;
			while (itr != null && itr.hasNext()) {
				match = (PersonIdentityTraits) itr.next();
				traitsKeyedByVPID.put(match.getVpid().getVPID(), match);
			}
		}
		return traitsKeyedByVPID;
	}



	/**
	 * Set the case status to Assigned if it is new and assigned to is not null
	 * @param wkfCase
	 * @throws ServiceException
	 */
    private void updateStatus(WorkflowCase wkfCase) throws ServiceException{

    	Assignment assignment = wkfCase.getAssignment();
    	if (assignment.getAssignedTo() != null) {
	 	   //Set the status to assigned
	 	   Status curStatus = wkfCase.getStatus();
	 	   if (WkfCaseStatusType.NEW.getName().equals(curStatus.getStatusValue().getCode())){
	 		   //Set it to assigned state
	 		   curStatus.setStatusValue(
	 		       getLookupService().getWkfCaseStatusTypeByCode(
	 		           WkfCaseStatusType.ASSIGNED.getName()));
	 	   }
	 	   if (assignment.getAssignmentDate() == null){
	 		  assignment.setAssignmentDate(DateUtils.getCurrentDate());
	 	   }
    	}
    	//Set assigned date to null if not assigned
    	else {
    		assignment.setAssignmentDate(null);
    	}

    	//set default facility to HEC
    	if (wkfCase.getRequestSource() == null) {
        	wkfCase.setRequestSource(getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode()));
    	}
    }

	public WorkflowDAO getWorkflowDAO() {
		return workflowDAO;
	}

	public void setWorkflowDAO(WorkflowDAO workflowDAO) {
		this.workflowDAO = workflowDAO;
	}

    public PersonDAO getPersonDAO() {
		return personDAO;
	}

	public void setPersonDAO(PersonDAO personDAO) {
		this.personDAO = personDAO;
	}

	/**
     * Returns current assignment or newly created assignment if it is null
     * @param wkfCase
     * @return
     */
    private Assignment getAssignment(WorkflowCase wkfCase){
        Assignment assignment = wkfCase.getAssignment();
        if (assignment == null){
            assignment = new Assignment ();
            wkfCase.setAssignment(assignment);
        }
        return assignment;
    }
}