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

// Java Classes

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.exception.ExceptionUtils;

import gov.va.med.esr.common.batchprocess.CampLejeuneImportData;
import gov.va.med.esr.common.model.ee.CampLejeuneVerification;
import gov.va.med.esr.common.model.ee.Eligibility;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.lookup.EligibilityFactor;
import gov.va.med.esr.common.model.lookup.Indicator;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.person.id.PersonEntityKey;
import gov.va.med.esr.common.rule.service.EligibilityFactorRuleService;
import gov.va.med.esr.service.CampLejeuneService;
import gov.va.med.esr.service.EEResultInfo;
import gov.va.med.esr.service.EligibilityEnrollmentService;
import gov.va.med.esr.service.LookupService;
import gov.va.med.esr.service.PersonIdentityTraits;
import gov.va.med.esr.service.PersonService;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.fw.cache.EntityCacheManager;
import gov.va.med.fw.cache.TriggerEventCacheManager;
import gov.va.med.fw.model.AbstractEntity;
import gov.va.med.fw.rule.RuleValidationException;
import gov.va.med.fw.service.CLEARImportSuccessErrorReportException;
import gov.va.med.fw.service.ServiceException;


/**
 * Class that implements Preferred Facility services
 */
public class CampLejeuneServiceImpl extends AbstractRuleAwareServiceImpl implements CampLejeuneService {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 7087682947713664526L;
	
	private EligibilityEnrollmentService eligibilityEnrollmentService = null;
	
	private TriggerEventCacheManager triggerEventCacheManager;
	   
	private EntityCacheManager entityCacheManager;
	
	private static final String CL_ERROR_REASON_NOT_SUPPORT = "ES data on file does not support CL eligibility.";
	
	private static final String CL_ERROR_REASON_UPDATE_INELIGIBLE = "ES CL eligibility updated with CLEAR ineligibility.";

	/**
	 * get the person record from CLEAR Import data
	 * @param data
	 * @return
	 * @throws ServiceException
	 */
	public Person findPersonWithCLEARData(CampLejeuneImportData data)throws ServiceException {
		Validate.notNull(data, "Missing required CampLejeuneImportData");
		
		PersonIdentityTraits traits = data.getPersonTraits(getLookupService());
		
		Person onFile = getPersonService().find(traits);
		
		data.setIcn(onFile.getVPIDEntityKey().getShortVPID());
		
		Person clearPerson = (Person) onFile.clone();
		
		CampLejeuneVerification clv = data.getCampLejeuneVerification(getLookupService());
		
		clearPerson.setCampLejeuneVerification(clv);
		
		return clearPerson;
		
	}
	
	
	/**
	 * process the person record from CLEAR Import person with Enrollment business rules 
	 * @param incoming Person with CLEAR data, onFile Person found in ES record
	 * @return String[] - [0] old priority group, [1] new priority group, [2] reason txt for creating an exception record with reason. Default are three empty Strings "".
	 * @throws ServiceException
	 */
	
	public String[] processCLEARData(Person incoming) throws ServiceException {
        
		String[] oldNewErrString = {"", "", ""}; //initialize the return string
		
		Person onFile = this.getPersonService().getPerson( incoming.getPersonEntityKey() );
		//set the old priority group string
		oldNewErrString[0] = getEnrollmentPriority(onFile.getEnrollmentDetermination());
		
		VAFacility sendingFacility = getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode());

		// Process rules related to eligibility factor
		EEResultInfo clvInfo = new EEResultInfo();
		this.getEligibilityFactorRuleService().processSpecialFactors(incoming, sendingFacility, onFile, false, clvInfo);
		
		//CCR 13815: set the error reason for exception record, but continue processing
        //String errReason = sendingFacility.getErrorReason();
        String errReason = clvInfo.getCLVErrorReason();
		if (errReason != null) {
			// these two cases are considered successful cases, make sure the
			// caller capture and handle it specifically ut need to produce a record for detail unsuccessful report:
			// Create an exception record with reason
			if (CL_ERROR_REASON_NOT_SUPPORT.equals(errReason)) {
				// set the error reason
				oldNewErrString[2] = CL_ERROR_REASON_NOT_SUPPORT;
				logger.error("#### " + CL_ERROR_REASON_NOT_SUPPORT);
			} else if (CL_ERROR_REASON_UPDATE_INELIGIBLE.equals(errReason)) {
				// CCR 13769: remove Camp Lejeune from other eligibility factors
				if (onFile.getEnrollmentDetermination() != null) {
					onFile.getEnrollmentDetermination().removeCLFromOtherEligibilities();
				}
				// set the error reason
				oldNewErrString[2] = CL_ERROR_REASON_UPDATE_INELIGIBLE;
				logger.error("#### " + CL_ERROR_REASON_UPDATE_INELIGIBLE);
			}
		}
		
		// Provide the onFile person (data all merged, etc.) and calculate E&E. We
        // want to trigger letters so we invoke assessEEImpact by passing false.
		Person calculatedPerson = this.getEligibilityEnrollmentService().assessEEImpact(onFile, false, false, sendingFacility);
		
		//set the new priority group string
		oldNewErrString[1] = getEnrollmentPriority(calculatedPerson.getEnrollmentDetermination());

		
        Person pristinePerson = (Person)entityCacheManager.getItem(calculatedPerson.getEntityKey());
        
        if (isSendingZ11(oldNewErrString, calculatedPerson.getCampLejeuneVerification(), pristinePerson.getCampLejeuneVerification())) {
        	
        	PersonTriggerEvent z11TriggerEvt= new PersonTriggerEvent(PersonTrigger.DestinationType.MESSAGING,
                    PersonTrigger.TargetType.VISTA, PersonTrigger.DispatchType.NOTIFY,
                    PersonTrigger.DataType.ELIGIBILITY);
        	z11TriggerEvt.setPersonId((PersonEntityKey)pristinePerson.getEntityKey());
        	z11TriggerEvt.setIdentityTraits(pristinePerson.getIdentityTraits());
        	
        	Set evts= triggerEventCacheManager.getTriggerEvents();
        	if (evts == null) {
        		Set evtSet = new HashSet();
        		evtSet.add(z11TriggerEvt);
        		triggerEventCacheManager.storeTriggerEvents(evtSet);
        	} else {
        		evts.add(z11TriggerEvt);
        	}
        }
        
        //If the incoming message is the same as the one on file, do not persist
        if(AbstractEntity.matchesDomainValues(pristinePerson, calculatedPerson)) {
           triggerEventCacheManager.removeTriggerEvents();
        }
        else {
        	// Persist person
        	this.getPersonService().save(calculatedPerson);
        }
        
        if(logger.isDebugEnabled()) {
     	   logger.debug( "CampLejeuneServiceImpl.processCLEARData(): END processing CLEAR DATA rules for person VPID= " +calculatedPerson.getVPIDValue());
        }
        
        return oldNewErrString;
	}
	
	/*
2.6.5.1	The ES system shall send an HL7 message to all VistA sites of record when a change in Camp Lejeune Eligibility indicator occurs (i.e., “Yes to No”, “No to Yes”, or “NULL to a Yes or No”) or the Veteran’s enrollment is changed by ES as a result of processing Camp Lejeune information. 

2.6.5.2	The Enrollment System shall send the following information to all sites of record (i.e., via an HL7 Z11 message):
•	Camp Lejeune Eligibility indicator 
•	Camp Lejeune Date Registered
•	Camp Lejeune Eligibility Change Site
•	Camp Lejeune Eligibility Source of Change
	 */
	private boolean isSendingZ11(String[] oldNewErrString, CampLejeuneVerification result, CampLejeuneVerification pristine) {
		
		if (!oldNewErrString[0].equals(oldNewErrString[1]))
			return true; //priority group changed
			
		Indicator res = (result == null) ? null : result.getSpecialFactorIndicator();
		Indicator pri = (pristine == null) ? null : pristine.getSpecialFactorIndicator();
		
		if (res == null && pri == null)
			return false; //both null
		
		if ( (res == null && pri != null) || (res != null && pri == null) )
			return true; //one null, the other not null;
		
		//both not null
		return !res.getCode().equals(pri.getCode());
		
	}
	
    private String getEnrollmentPriority(EnrollmentDetermination enroll)
    {
        if (enroll != null && enroll.getPriorityGroup() != null)
        {
            String priority = enroll.getPriorityGroup().getDescription();

            if (enroll.getPrioritySubGroup() != null)
                priority += enroll.getPrioritySubGroup().getDescription();
            priority = priority.replaceAll("Enrollment Priority sub-Group", "");
            return priority.replaceAll("'", "");
        }
        return "";
    }

	public EligibilityEnrollmentService getEligibilityEnrollmentService() {
		return eligibilityEnrollmentService;
	}

	public void setEligibilityEnrollmentService(EligibilityEnrollmentService eligibilityEnrollmentService) {
		this.eligibilityEnrollmentService = eligibilityEnrollmentService;
	}

	public TriggerEventCacheManager getTriggerEventCacheManager() {
		return triggerEventCacheManager;
	}

	public void setTriggerEventCacheManager(TriggerEventCacheManager triggerEventCacheManager) {
		this.triggerEventCacheManager = triggerEventCacheManager;
	}

	public EntityCacheManager getEntityCacheManager() {
		return entityCacheManager;
	}

	public void setEntityCacheManager(EntityCacheManager entityCacheManager) {
		this.entityCacheManager = entityCacheManager;
	}

}
