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

// Java classes

// Library classes
import gov.va.med.esr.common.model.ee.EligibilityVerification;
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.RatedDisability;
import gov.va.med.esr.common.model.ee.ServiceConnectionAward;
import gov.va.med.esr.common.model.ee.VerificationInfo;
import gov.va.med.esr.common.model.lookup.AACIndicator;
import gov.va.med.esr.common.model.lookup.Disability;
import gov.va.med.esr.common.model.lookup.EligibilityStatus;
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.MessageType;
import gov.va.med.esr.common.model.lookup.VAFacility;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.util.RuleAbstractTestCase;
import gov.va.med.esr.service.PersonHelperService;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.lang.Validate;

/**
 * ProcessVBAFromZ07Test created on Aug 21, 2006
 * 
 * @author DNS   LEV
 */
public class ProcessVBAForZ07Test extends RuleAbstractTestCase {

	private static Person onfile = null;

	/**
	 * A default constructor
	 * 
	 * @param testName
	 */
	public ProcessVBAForZ07Test(String testName) throws Exception {
		super(testName);
	}

	/**
	 * Test the following business requirement when a ESR receives a ORUZ07
	 * message from a site.
	 * 
	 * 4161 [UC51.3.4.1] For a new person the EDB will accept the site
	 * eligibility data as it exists on the HL7. The system will set the
	 * Eligibility Status to Pending Verification and the system will set the
	 * Eligibility, Eligibility Status Date, Eligibility Status Method, and the
	 * Eligibility Verification Source to Null.
	 * 
	 * @throws Exception
	 *             in case of errors in processing a a rule
	 */
	public void testIsNewPerson() throws Exception {

		// Simulate an incoming person
		Person incoming = (Person) onfile.clone();

		// Simulate an updated person. An updated person is
		// a person that has been updated in other rule flows.
		// For this rule, there is no need to call other rule flows
		// to update an incoming person.
		Person updated = (Person) onfile.clone();

		try {
			VerificationInfo info = new VerificationInfo("01", "ORUZ07-E", null);

			// Invoke ProcessVBAFromZ07 rule flow
			this.getProcessVBARuleService().processVBAData(incoming, null, updated,
					info);

			// Check the updated person to see if isNewVeteran rule is invoked
			// correctly
			EligibilityVerification ev = updated.getEligibilityVerification();

			// EV must not be null
			assertNotNull("Eligibility Verification must not be null", ev);

			EligibilityStatus es = ev.getEligibilityStatus();

			// ES must not be null
			assertNotNull("Eligibility Status must not be null", es);

			// ES code must be EligibilityStatus.CODE_PENDING_VERIFICATION
			assertTrue("Eligibility status must be PERNDING",
					EligibilityStatus.CODE_PENDING_VERIFICATION.getCode()
							.equals(es.getCode()));

			// Status date must be null
			assertTrue("Status date must be null",
					(ev.getEligibilityStatusDate() == null));

			// Verification method must be null
			assertTrue("Verification method must be null",
					(ev.getVerificationMethod() == null));

			// verification source must be null
			assertTrue("Verification source must be null",
					(ev.getVerificationSource() == null));
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Got an un-expected validation exception ", e);
			}
			this.fail("Got an un-expected validation exception", e);
		}
	}

	/**
	 * Test the following business requirement when a ESR receives a ORUZ07
	 * message from a site.
	 * 
	 * 4163 [UC51.3.4.8] For new and existing persons, if the EDB Claim Number
	 * and/or the Claim Folder Location are null accept the claim folder number
	 * and claim folder location on the HL7 data.
	 * 
	 * @throws Exception
	 *             in case of errors in processing a a rule
	 */
	public void testUpdateClaimFolder() throws Exception {

		// Test a new person

		// Simulate an incoming person
		Person incoming = (Person) onfile.clone();

		// Set claim folder number and location
		VAFacility facility = this.getRandomVAFacility();
		String number = "555";

		incoming.setClaimFolderLocation(facility);
		incoming.setClaimFolderNumber(number);

		// Simulate an updated person. An updated person is
		// a person that has been updated in other rule flows.
		// For this rule, there is no need to call other rule flows
		// to update an incoming person.
		Person updated = (Person) onfile.clone();

		try {
			VerificationInfo info = new VerificationInfo("01", "ORUZ07-E", null);

			// Invoke ProcessVBAFromZ07 rule flow
			this.getProcessVBARuleService().processVBAData(incoming, facility, updated,
					info);

			// Folder Number must be set to value from incoming person
			assertTrue("Folder number must be equal to incoming value",
					number.equals(updated.getClaimFolderNumber()));

			// Folder location must be set to value from incoming person
			assertTrue("Folder location must be equal to incoming value",
					facility.equals(updated.getClaimFolderLocation()));
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Got an un-expected validation exception ", e);
			}
			this.fail("Got an un-expected validation exception", e);
		}

		// Test an existing person
		onfile.setEnrollmentDetermination(this.createEnrollmentDetermination());
		this.getPersonService().save(onfile);

		// Simulate an incoming person
		incoming = (Person) onfile.clone();

		// Set claim folder number and location
		facility = this.getRandomVAFacility();
		number = "555";

		incoming.setClaimFolderLocation(facility);
		incoming.setClaimFolderNumber(number);

		// Simulate an updated person. An updated person is
		// a person that has been updated in other rule flows.
		// For this rule, there is no need to call other rule flows
		// to update an incoming person.
		updated = (Person) onfile.clone();

		try {
			VerificationInfo info = new VerificationInfo("01", "ORUZ07-E", null);

			// Invoke ProcessVBAFromZ07 rule flow
			this.getProcessVBARuleService().processVBAData(incoming, facility, updated,
					info);

			// Folder Number must be set to value from incoming person
			assertTrue("Folder number must be equal to incoming value",
					number.equals(updated.getClaimFolderNumber()));

			// Folder location must be set to value from incoming person
			assertTrue("Folder location must be equal to incoming value",
					facility.equals(updated.getClaimFolderLocation()));
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Got an un-expected validation exception ", e);
			}
			this.fail("Got an un-expected validation exception", e);
		}

		// Remove enrollment determination
		onfile.setEnrollmentDetermination(null);
	}

	/**
	 * Test the following business requirement when a ESR receives a ORUZ07
	 * message from a site.
	 * 
	 * 5220 [UC51.3.4.11] When accepting Claim Folder Number and Claim Folder
	 * Location from an incoming message, if the claim folder number is
	 * populated but Claim Folder Location is not, assign a location of 376
	 * 
	 * @throws Exception
	 *             in case of errors in processing a a rule
	 */
	public void testDefaultClaimFolder() throws Exception {

		// Test a new person

		// Simulate an incoming person
		Person incoming = (Person) onfile.clone();

		// Use default facility when not specified
		VAFacility facility = this
				.getVaFacility(VAFacility.CODE_DEFAULT_CLAIM_FOLDER_LOCATION);

		// Set claim folder number
		String number = "555";
		incoming.setClaimFolderNumber(number);

		// Simulate an updated person. An updated person is
		// a person that has been updated in other rule flows.
		// For this rule, there is no need to call other rule flows
		// to update an incoming person.
		Person updated = (Person) onfile.clone();

		try {
			VerificationInfo info = new VerificationInfo("01", "ORUZ07-E", null);

			// Invoke ProcessVBAFromZ07 rule flow
			this.getProcessVBARuleService().processVBAData(incoming, facility, updated,
					info);

			// Folder Number must be set to value from incoming person
			assertTrue("Folder number must be equal to incoming value",
					number.equals(updated.getClaimFolderNumber()));

			// Folder location must be set to value from incoming person
			assertTrue("Folder location must be equal to incoming value",
					facility.equals(updated.getClaimFolderLocation()));
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Got an un-expected validation exception ", e);
			}
			this.fail("Got an un-expected validation exception", e);
		}

		// Test an existing person

		// Create a simple person with an enrollment record
		onfile.setEnrollmentDetermination(this.createEnrollmentDetermination());
		this.getPersonService().save(onfile);

		// Simulate an incoming person
		incoming = (Person) onfile.clone();

		// Set claim folder and location
		number = "555";
		incoming.setClaimFolderNumber(number);

		// Simulate an updated person. An updated person is
		// a person that has been updated in other rule flows.
		// For this rule, there is no need to call other rule flows
		// to update an incoming person.
		updated = (Person) onfile.clone();

		try {
			VerificationInfo info = new VerificationInfo("01", "ORUZ07-E", null);

			// Invoke ProcessVBAFromZ07 rule flow
			this.getProcessVBARuleService().processVBAData(incoming, facility, updated,
					info);

			// Folder Number must be set to value from incoming person
			assertTrue("Folder number must be equal to incoming value",
					number.equals(updated.getClaimFolderNumber()));

			// Folder location must be set to value from incoming person
			assertTrue("Folder location must be equal to incoming value",
					facility.equals(updated.getClaimFolderLocation()));
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Got an un-expected validation exception ", e);
			}
			this.fail("Got an un-expected validation exception", e);
		}

		// Remove enrollment determination
		onfile.setEnrollmentDetermination(null);
	}

	/**
	 * Test the following business requirement when a ESR receives a ORUZ07
	 * message from a site.
	 * 
	 * 5068 [UC51.3.4.2] For an existing person where the VBA Query is still
	 * pending a response from VBA, ESR will accept site data for a SC 0%
	 * Non-Compensable or a NSC Non-Compensable veteran if the data in the HL7
	 * meets the following criteria:
	 * 
	 * If the Eligibility Status is VERIFIED AND the veteran is NSC (Veteran
	 * Indicator = Yes, there are no SC conditions present, Total Check Amount =
	 * $0 or NULL and VA Pension is No or NULL THEN The incoming data should
	 * replace the current site verification data that is on file.
	 * 
	 * If present, accept the Ineligible Date, Ineligible Reason, VARO Decision
	 * from the site record. Note that if Ineligible Date, Ineligible Reason
	 * and/or VARO Decision are populated, set Veteran Indicator = NO.
	 * 
	 * The system will set the following values:
	 * 
	 * A&A Indicator = No Housebound Indicator = No VA Pension = No Receiving VA
	 * Disability Compensation = No Total Check Amount = NULL Unemployable = No
	 * P&T Indicator = No P&T Effective Date = NULL Military Disability
	 * Retirement = No Discharge Due to Disability = value from the site
	 * Eligibility Status = Pending Verification Eligibility Status Date = NULL
	 * Eligibility Verification Method = NULL Eligibility Verification Source =
	 * NULL
	 * 
	 * @throws Exception
	 *             in case of errors in processing a a rule
	 */
	public void isNSCNonCompensable() throws Exception {

		// Create a veteran with an enrollment record
		// and a VBA Queried - Pending Response status
		onfile.setEnrollmentDetermination(this.createEnrollmentDetermination());
		this.setAACIndicator(AACIndicator.CODE_AAC_QUERIED_PENDING.getCode(),
				onfile);
		this.getPersonService().save(onfile);

		// Simulate an incoming person
		Person incoming = (Person) onfile.clone();

		// Set verified eligibility status
		EligibilityVerification ev = this.createEligibilityVerification();
		ev.setEligibilityStatus(getLookupService().getEligibilityStatusByCode(
				EligibilityStatus.CODE_VERIFIED.getCode()));
		incoming.setEligibilityVerification(ev);

		// Set veteran flag
		incoming.setVeteran(Boolean.TRUE);

		// Set NSC non-compensable
		incoming.setServiceConnectionAward(null);
		incoming.setMonetaryBenefitAward(null);

		// Simulate an updated person. An updated person is
		// a person that has been updated in other rule flows.
		// For this rule, there is no need to call other rule flows
		// to update an incoming person.
		Person updated = (Person) onfile.clone();

		try {
			VerificationInfo info = new VerificationInfo("01", "ORUZ07-E", null);

			// Invoke ProcessVBAFromZ07 rule flow
			this.getProcessVBARuleService().processVBAData(incoming, getRandomVAFacility(), updated,
					info);

			PersonHelperService helper = this.getPersonHelperService();
			MonetaryBenefit mb = helper.getAABenefit(updated);
			assertNotNull("AA MonetaryBenefit must not be null", mb);

			Indicator ind = mb.getMonetaryBenefitIndicator();
			assertNotNull("AA MonetaryBenefit Indicator must not be null", ind);

			assertTrue("AA MonetaryBenefit Indicator must be to NO",
					Indicator.NO.getCode().equals(ind.getCode()));

			mb = helper.getHouseboundBenefit(updated);
			assertNotNull("Housebound MonetaryBenefit must not be null", mb);

			ind = mb.getMonetaryBenefitIndicator();
			assertNotNull(
					"Housebound MonetaryBenefit Indicator must not be null",
					ind);

			assertTrue("Housebound MonetaryBenefit Indicator must be to NO",
					Indicator.NO.getCode().equals(ind.getCode()));

			mb = helper.getVAPensionBenefit(updated);
			assertNotNull("VA Pension MonetaryBenefit must not be null", mb);

			ind = mb.getMonetaryBenefitIndicator();
			assertNotNull(
					"VA Pension MonetaryBenefit Indicator must not be null",
					ind);

			assertTrue("VA Pension MonetaryBenefit Indicator must be to NO",
					Indicator.NO.getCode().equals(ind.getCode()));

			mb = helper.getDisabilityBenefit(updated);
			assertNull("VA Disability benefit must be null", mb);

			MonetaryBenefitAward mba = updated.getMonetaryBenefitAward();
			assertNotNull("MonetaryBenefitAward must not be null", mba);
			assertTrue("Total check amount must be null",
					(mba.getCheckAmount() == null));

		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Got an un-expected validation exception ", e);
			}
			this.fail("Got an un-expected validation exception", e);
		}

		// Remove enrollment determination
		onfile.setEnrollmentDetermination(null);
		ev = onfile.getEligibilityVerification();
		if (ev != null) {
			ev.setAacIndicator(null);
		}
	}

	/**
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		Validate.notNull(this.getProcessVBARuleService(),
				"A process vba rule service is required");
	}

	/**
	 * @see gov.va.med.fw.util.AbstractTestCase#customSetUp()
	 */
	protected void customSetUp() throws Exception {
		super.customSetUp();

		if (onfile == null) {
			// Create a simple person without an enrollment
			// determination record. This means that this is
			// not an enrolled veteran.
			onfile = this.buildSimplePerson();
			this.getPersonService().save(onfile);
		}
	}
}