/********************************************************************
 * Copyright © 2006 VHA. All rights reserved
 ********************************************************************/


package gov.va.med.esr.common.rule.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import gov.va.med.esr.common.infra.ImpreciseDate;
import gov.va.med.esr.common.model.ee.AgentOrangeExposure;
import gov.va.med.esr.common.model.ee.Eligibility;
import gov.va.med.esr.common.model.ee.EligibilityVerification;
import gov.va.med.esr.common.model.ee.EnrollmentDetermination;
import gov.va.med.esr.common.model.ee.MilitaryService;
import gov.va.med.esr.common.model.ee.MilitaryServiceSiteRecord;
import gov.va.med.esr.common.model.ee.MonetaryBenefit;
import gov.va.med.esr.common.model.ee.MonetaryBenefitAward;
import gov.va.med.esr.common.model.ee.RadiationExposure;
import gov.va.med.esr.common.model.ee.RatedDisability;
import gov.va.med.esr.common.model.ee.ServiceConnectionAward;
import gov.va.med.esr.common.model.lookup.AddressType;
import gov.va.med.esr.common.model.lookup.Country;
import gov.va.med.esr.common.model.lookup.EligibilityStatus;
import gov.va.med.esr.common.model.lookup.EligibilityType;
import gov.va.med.esr.common.model.lookup.EnrollmentStatus;
import gov.va.med.esr.common.model.lookup.Indicator;
import gov.va.med.esr.common.model.lookup.InsuredRelationship;
import gov.va.med.esr.common.model.lookup.MessageType;
import gov.va.med.esr.common.model.lookup.MonetaryBenefitType;
import gov.va.med.esr.common.model.lookup.ServicePeriod;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.party.Address;
import gov.va.med.esr.common.model.person.BirthRecord;
import gov.va.med.esr.common.model.person.DeathRecord;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.util.RuleAbstractTestCase;
import gov.va.med.esr.service.trigger.PersonTrigger;
import gov.va.med.esr.service.trigger.PersonTriggerEvent;
import gov.va.med.esr.service.trigger.PersonTriggerIdentity;
import gov.va.med.fw.hl7.Message;
import gov.va.med.fw.hl7.segment.IN1;
import gov.va.med.fw.hl7.segment.ZMT;
import gov.va.med.fw.util.DateUtils;
import gov.va.med.fw.validation.ValidationFieldMessage;
import gov.va.med.fw.validation.ValidationMessages;

/**
 * Initial javadoc for class ConsistencyCheckRuleServiceTest.
 * TODO - Add content here
 * 
 * Created Aug 8, 2006 1:03:10 PM
 * @author DNS   BOHMEG
 */
public class ConsistencyCheckRuleServiceTest extends RuleAbstractTestCase {
	private ConsistencyCheckRuleService ccRuleService;
	private String NON_NULL_VALUE = "dfasdf";
	private String testPersonId = "66512";
	
	private void passConsistencyChecks(Person person, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageTypeCode) throws Exception {
		try {
			ccRuleService.processConsistencyChecksForMessage(person, triggerEvent, message, messageTypeCode);
		} catch(ConsistencyCheckException e) {
			System.out.println("Did not expect to receive these CC problems: ");
			dumpCC(e.getValidationMessages(), messageTypeCode);
			throw e;
		}
	}
	
	private void failConsistencyChecks(int expectedFailures, Person person, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageTypeCode) throws Exception {
		System.out.println("##failConsistencyChecks, expecting: " + expectedFailures);
		ValidationMessages msgs = null;
		try {
			ccRuleService.processConsistencyChecksForMessage(person, triggerEvent, message, messageTypeCode);
		} catch(ConsistencyCheckException e) {
			msgs = e.getValidationMessages();			
			System.out.println(messageTypeCode.getCode() + " : Expected CC failed with blocking: " + e.isBlocking());
			dumpCC(msgs, messageTypeCode);
		}
        if (expectedFailures > 0) {
            assertNotNull("Failed to fire CC", msgs);
            assertEquals(expectedFailures, (msgs != null) ? msgs.getCount() : 0);            
        }
	}
	
	private void dumpCC(ValidationMessages msgs, MessageType.Code messageTypeCode) {
		Iterator itr = msgs.get();
		ValidationFieldMessage msg = null;
		while(itr.hasNext()) {
			msg = (ValidationFieldMessage) itr.next();
			System.out.println(messageTypeCode.getCode() + " : Received CC error message [" + msg.getKey() + "] with contextInfo [" +
					msg.getContextInfo() + "]");
		}		
	}
	
	public void testOutboundZ11CC() throws Exception {
		Message message = null; // not tested yet from the JUnit
		PersonTriggerEvent triggerEvent = null; // not tested yet from the JUnit
		
		MessageType.Code messageType = MessageType.CODE_ORUZ11_TO_SITE;
		
		Person person = getPerson(testPersonId);		
		// should pass with this data
		MilitaryServiceSiteRecord siteRec = new MilitaryServiceSiteRecord();
		siteRec.setServicePeriod(getLookupService().getServicePeriodByCode(ServicePeriod.CODE_KOREAN.getCode()));
		siteRec.setSite(getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode()));
		person.getMilitaryService().addMilitaryServiceSiteRecord(siteRec);
		
		EligibilityVerification ev = new EligibilityVerification();
		person.setEligibilityVerification(ev);
		ev.setEligibilityStatus(
				getLookupService().getEligibilityStatusByCode(EligibilityStatus.CODE_VERIFIED.getCode()));
		ev.setEligibilityStatusDate(new ImpreciseDate("8/14/2006"));
		ev.setVerificationMethod("test");
		
		MonetaryBenefitAward mba = new MonetaryBenefitAward();
		mba.setCheckAmount(new BigDecimal("123.45"));
		MonetaryBenefit mb = new MonetaryBenefit();
		mb.setMonetaryBenefitIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
		mb.setType(getLookupService().getMonetaryBenefitTypeByCode(MonetaryBenefitType.CODE_HOUSEBOUND.getCode()));
		mba.addMonetaryBenefit(mb);
		person.setMonetaryBenefitAward(mba);
		
		Eligibility elig = new Eligibility();
		elig.setType(getLookupService().getEligibilityTypeByCode(EligibilityType.WORLD_WAR_I.getCode()));
		EnrollmentDetermination ed = new EnrollmentDetermination();
		ed.setPrimaryEligiblity(elig);		
		
		passConsistencyChecks(person, null, null, messageType);
		
		// break it up into little tests to get full coverage
		failZ11ConsistencyChecks1((Person) person.clone(), triggerEvent, message, messageType);
		failZ11ConsistencyChecks2((Person) person.clone(), triggerEvent, message, messageType);
		failZ11ConsistencyChecks3((Person) person.clone(), triggerEvent, message, messageType);
		failZ11ConsistencyChecks4((Person) person.clone(), triggerEvent, message, messageType);
        passZ11ConsistencyChecks5((Person) person.clone(), triggerEvent, message, messageType);        
	}
	
	private void failZ11ConsistencyChecks1(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {		
		// fail condition: future DOD
		DeathRecord dod = new DeathRecord();
		dod.setDeathDate(new ImpreciseDate(DateUtils.getTomorrowDate()));
		passingPerson.setDeathRecord(dod);
		// fail condition: born too late for WW1 eligibility
		BirthRecord dob = new BirthRecord();
		dob.setBirthDate(new ImpreciseDate("01/01/1950"));
		passingPerson.setBirthRecord(dob); 
		Eligibility elig = new Eligibility();
		elig.setType(getLookupService().getEligibilityTypeByCode(EligibilityType.WORLD_WAR_I.getCode()));
		EnrollmentDetermination ed = new EnrollmentDetermination();
		ed.setPrimaryEligiblity(elig);
		passingPerson.setEnrollmentDetermination(ed);
		// fail condition: missing ineligible reason
		ed.setEnrollmentStatus(getLookupService().getEnrollmentStatusByCode(
				EnrollmentStatus.CODE_NOT_ELIGIBLE_INELIGIBLE_DATE.getCode()));
		passingPerson.setIneligibilityFactor(null);
		// fail condition: SC must have "valid" RatedDisabilities (code and percentage must be present)
		ServiceConnectionAward sca = new ServiceConnectionAward();
		sca.setServiceConnectedIndicator(Boolean.TRUE);
		sca.setServiceConnectedPercentage(new Integer(50));
		passingPerson.setServiceConnectionAward(sca);
		RatedDisability rd = new RatedDisability();
		sca.addRatedDisability(rd);
		// fail condition: Period of Service is not valid for primary eligibility of WW 1
		MilitaryService ms = new MilitaryService();
		MilitaryServiceSiteRecord siteRec = new MilitaryServiceSiteRecord();
		siteRec.setServicePeriod(getLookupService().getServicePeriodByCode(ServicePeriod.CODE_AIR_FORCE.getCode()));
		siteRec.setSite(getLookupService().getVaFacilityByCode(VAFacility.CODE_HEC.getCode()));
		ms.addMilitaryServiceSiteRecord(siteRec);
		passingPerson.setMilitaryService(ms);
		// fail test
		failConsistencyChecks(5, passingPerson, triggerEvent, message, messageType);
	}
	
	private void failZ11ConsistencyChecks2(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {		
		// fail condition: born too late for Mexican Border War
		BirthRecord dob = new BirthRecord();
		dob.setBirthDate(new ImpreciseDate("01/01/1980"));
		passingPerson.setBirthRecord(dob); 
		Eligibility elig = new Eligibility();
		elig.setType(getLookupService().getEligibilityTypeByCode(EligibilityType.MEXICAN_BORDER_WAR.getCode()));
		EnrollmentDetermination ed = new EnrollmentDetermination();
		ed.setPrimaryEligiblity(elig);
		passingPerson.setEnrollmentDetermination(ed);
		// fail condition: missing ineligible reason
		ed.setEnrollmentStatus(getLookupService().getEnrollmentStatusByCode(
				EnrollmentStatus.CODE_NOT_ELIGIBLE_INELIGIBLE_DATE.getCode()));
		passingPerson.setIneligibilityFactor(null);
		// fail condition: missing AOE location
		AgentOrangeExposure aoe = new AgentOrangeExposure();
		aoe.setSpecialFactorIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
		passingPerson.setAgentOrangeExposure(aoe);
		// fail condition: check amount without a type
		MonetaryBenefitAward mba = new MonetaryBenefitAward();
		mba.setCheckAmount(new BigDecimal("123.45"));
		passingPerson.setMonetaryBenefitAward(mba);		
		// fail test
		failConsistencyChecks(4, passingPerson, triggerEvent, message, messageType);
	}
	
	private void failZ11ConsistencyChecks3(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {
		EnrollmentDetermination ed = new EnrollmentDetermination();
		passingPerson.setEnrollmentDetermination(ed);
		
		// fail condition: missing cancel decline reason
		ed.setEnrollmentStatus(getLookupService().getEnrollmentStatusByCode(
				EnrollmentStatus.CODE_CANCELLED_DECLINED.getCode()));
		passingPerson.setCancelDecline(null);
		// fail condition: receiving disability invalid b/c NSC
		MonetaryBenefitAward mba = new MonetaryBenefitAward();
		passingPerson.setMonetaryBenefitAward(mba);				
		MonetaryBenefit mb = new MonetaryBenefit();
		mb.setMonetaryBenefitIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
		mb.setType(getLookupService().getMonetaryBenefitTypeByCode(MonetaryBenefitType.CODE_DISABILITY_COMPENSATION.getCode()));
		mba.addMonetaryBenefit(mb);		
		Eligibility elig = new Eligibility();
		elig.setType(getLookupService().getEligibilityTypeByCode(EligibilityType.NSC.getCode()));		
		ed.addSecondaryEligibility(elig);
		// fail condition: SC can not have NSC as an Eligibility
		ServiceConnectionAward sca = new ServiceConnectionAward();
		sca.setServiceConnectedIndicator(Boolean.TRUE);
		passingPerson.setServiceConnectionAward(sca);		
		// fail condition: SC must have SC Percentage
		sca.setServiceConnectedPercentage(null);
		// fail condition: SC must have at least one RatedDisability
		sca.removeAllRatedDisability();
		// fail test
		failConsistencyChecks(5, passingPerson, triggerEvent, message, messageType);
	}
	
	private void failZ11ConsistencyChecks4(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {
		EnrollmentDetermination ed = new EnrollmentDetermination();
		passingPerson.setEnrollmentDetermination(ed);
			
		// fail condition: missing DOD
		ed.setEnrollmentStatus(getLookupService().getEnrollmentStatusByCode(
				EnrollmentStatus.CODE_DECEASED.getCode()));
		passingPerson.setDeathRecord(null);
		// fail condition: missing verification method
		EligibilityVerification ev = new EligibilityVerification();
		passingPerson.setEligibilityVerification(ev);
		ev.setEligibilityStatus(
				getLookupService().getEligibilityStatusByCode(EligibilityStatus.CODE_VERIFIED.getCode()));
		// fail condition: missing Period Of Service
		passingPerson.getMilitaryService().removeAllMilitaryServiceSiteRecords();
		// fail condition: missing Radiation Exposure Method
		RadiationExposure re = new RadiationExposure();
		re.setSpecialFactorIndicator(getLookupService().getIndicatorByCode(Indicator.YES));
		passingPerson.setRadiationExposure(re);		
		// fail test
		failConsistencyChecks(4, passingPerson, triggerEvent, message, messageType);				
	}
    
    private void passZ11ConsistencyChecks5(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {     
        EnrollmentDetermination ed = new EnrollmentDetermination();
        passingPerson.setEnrollmentDetermination(ed);
        ed.setEnrollmentStatus(getLookupService().getEnrollmentStatusByCode(
                EnrollmentStatus.CODE_NOT_ELIGIBLE_REFUSED_TO_PAY_COPAY.getCode()));
        passingPerson.setIneligibilityFactor(null);
        // no failures should occur. Defect 5157 was causing failure here
        failConsistencyChecks(0, passingPerson, triggerEvent, message, messageType);
    }    

	public void testOutboundZ04CC() throws Exception {
		MessageType.Code messageType = MessageType.CODE_ORUZ04_TO_SITE;
		Person person = null; // not used for the Z04 CC
		PersonTriggerEvent triggerEvent = null; // not used for the Z04 CC
		Message message = new Message();
		
		List segments = new ArrayList();
		IN1 seg = new IN1();
		segments.add(seg);
		message.setSegments(segments);
		seg.setInsuranceCompanyName("fadfd");
		seg.setGroupNumber("gdsfds");
		seg.setInsuredName("fdsfgsg");
		seg.setInsuredRelationToPatient("fsdfsd");
						
		passConsistencyChecks(person, triggerEvent, message, messageType);

		// fail condition: missing insurance company name
		seg.setInsuranceCompanyName(null);
		// fail condition: missing group number
		seg.setGroupNumber(null);
		// note the name is not required because the relationship is null
		seg.setInsuredName(null);
		// fail condition: missing relationship to patient
		seg.setInsuredRelationToPatient(null);
		// fail test
		failConsistencyChecks(3, person, triggerEvent, message, messageType);
		
		// fail condition: missing insured name b/c not Vet
		seg.setInsuredRelationToPatient(InsuredRelationship.CODE_OTHER.getCode());
		// fail test
		failConsistencyChecks(3, person, triggerEvent, message, messageType);								
	}

	public void testOutboundZ05CC() throws Exception {
		MessageType.Code messageType = MessageType.CODE_ORUZ05_TO_SITE;
		Person person = getPerson(testPersonId);
		PersonTriggerEvent triggerEvent = new PersonTriggerEvent(new PersonTriggerIdentity(
				PersonTrigger.DestinationType.MESSAGING,
				PersonTrigger.TargetType.VISTA,
				PersonTrigger.DispatchType.NOTIFY,
				PersonTrigger.DataType.ADDRESS));
		Message message = null; // not used for the Z05 CC

		Address add = new Address();
		add.setType(getLookupService().getAddressTypeByCode(AddressType.CODE_PERMANENT_ADDRESS.getCode()));
		add.setLine1(NON_NULL_VALUE);
		add.setCity(NON_NULL_VALUE);
		add.setZipCode(NON_NULL_VALUE);
		add.setState("NJ");
		add.setCountry(Country.CODE_USA.getCode());
		person.removeAllAddresses();
		person.addAddress(add);
		// should pass
		passConsistencyChecks(person, triggerEvent, message, messageType);
		
		failZ05ConsistencyChecks1((Person) person.clone(), triggerEvent, message, messageType);
		failZ05ConsistencyChecks2((Person) person.clone(), triggerEvent, message, messageType);
		failZ05ConsistencyChecks3((Person) person.clone(), triggerEvent, message, messageType);
	}
	
	private void failZ05ConsistencyChecks1(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {		
		// fail condition: missing line1
		// fail condition: missing city
		// fail condition: missing country
		passingPerson.removeAllAddresses();
			
		// fail test
		failConsistencyChecks(3, passingPerson, triggerEvent, message, messageType);				
	}	

	private void failZ05ConsistencyChecks2(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {
		passingPerson.removeAllAddresses();
		Address add = new Address();		
		add.setType(getLookupService().getAddressTypeByCode(AddressType.CODE_PERMANENT_ADDRESS.getCode()));
		passingPerson.addAddress(add);
		add.setCountry(Country.CODE_USA.getCode());
		// fail condition: invalid state for USA
		add.setState(NON_NULL_VALUE);
		// fail condition: missing line1
		// fail condition: missing city
		// fail condition: missing zip code
		
		// fail test
		failConsistencyChecks(4, passingPerson, triggerEvent, message, messageType);				
	}	

	private void failZ05ConsistencyChecks3(Person passingPerson, PersonTriggerEvent triggerEvent, Message message, MessageType.Code messageType) throws Exception {
		passingPerson.removeAllAddresses();
		Address add = new Address();
		add.setType(getLookupService().getAddressTypeByCode(AddressType.CODE_PERMANENT_ADDRESS.getCode()));
		passingPerson.addAddress(add);
		add.setLine1(NON_NULL_VALUE);
		add.setCity(NON_NULL_VALUE);
		add.setCountry(Country.CODE_PHILIPPINES.getCode());
		
		// make sure no foreign address CC fires
		failConsistencyChecks(0, passingPerson, triggerEvent, message, messageType);				
	}	
	
	public void testOutboundZ06CC() throws Exception {
		MessageType.Code messageType = MessageType.CODE_ORUZ06_TO_SITE;		
		Person person = null; // not used for the Z06 CC
		PersonTriggerEvent triggerEvent = null; // not used for the Z06 CC
		Message message = new Message();		
		
		List segments = new ArrayList();
		ZMT seg = new ZMT();
		segments.add(seg);
		message.setSegments(segments);
		seg.setMeansTestDate("fsdfsd");
		seg.setVeteranTestSignedDate("dfdsfsdf");
		
		// should pass
		passConsistencyChecks(person, triggerEvent, message, messageType);
		
		// fail condition: missing means test date
		seg.setMeansTestDate(null);
		// fail condition: missing date signed
		seg.setVeteranTestSignedDate(null);
		// fail test
		failConsistencyChecks(2, person, triggerEvent, message, messageType);				
	}

	public void testOutboundZ10CC() throws Exception {
		MessageType.Code messageType = MessageType.CODE_ORUZ10_TO_SITE;
		Person person = null; // not used for the Z10 CC
		PersonTriggerEvent triggerEvent = null; // not used for the Z10 CC
		Message message = null; // not used for the Z10 CC		
				
		// should always pass (regardless of input since there are no CC's for Z10)
		passConsistencyChecks(person, triggerEvent, message, messageType);
	}
	
	/**
	 * @return Returns the ccRuleService.
	 */
	public ConsistencyCheckRuleService getCcRuleService() {
		return ccRuleService;
	}
	/**
	 * @param ccRuleService The ccRuleService to set.
	 */
	public void setCcRuleService(ConsistencyCheckRuleService ccRuleService) {
		this.ccRuleService = ccRuleService;
	}
}
