package gov.va.med.esr.service;

import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Date;

import gov.va.med.esr.common.util.RuleAbstractTestCase;
import gov.va.med.esr.common.model.lookup.Condition;
import gov.va.med.esr.common.model.lookup.Diagnosis;
import gov.va.med.esr.common.model.lookup.Procedure;
import gov.va.med.esr.common.model.lookup.DescriptorType;
import gov.va.med.esr.common.model.lookup.CDDeterminationMethod;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.model.ee.CatastrophicDisability;
import gov.va.med.esr.common.model.ee.CDCondition;
import gov.va.med.esr.common.model.ee.CDProcedure;
import gov.va.med.esr.common.model.ee.CDDiagnosis;
import gov.va.med.esr.common.model.ee.CDDescriptor;
import gov.va.med.fw.service.ServiceException;

/**
 * Place to put all the CD Conversion related tests (other than the batch job)
 * 
 * @author DNS   ruizc
 *
 */
public class CDConversionLogicTest extends RuleAbstractTestCase {
	
	private Set<String> cc = new HashSet<String>();	
	private Set<String> pc = new HashSet<String>();
	private Set<String> dc = new HashSet<String>();	
	
	public CDConversionLogicTest(String arg0) {
		super(arg0);
	}	

	

	
	protected void customSetUp() throws Exception {
		super.customSetUp();
		
		cc.add(Condition.CODE_FIM.getCode());
		cc.add(Condition.CODE_FOLS.getCode());
		cc.add(Condition.CODE_GAF.getCode());
		cc.add(Condition.CODE_KATZ.getCode());
				
		/*
		 * <entry key="84.17" value="5" />
		 */
		pc.add("84.17");
		/*
		 * <entry key="84.12" value="5" />
		 */
		pc.add("84.12");
		/*
		 * <entry key="84.03" value="5" />
		 */
		pc.add("84.03");
		/*
		 * <entry key="84.07" value="5" />
		 */
		pc.add("84.07");
		/*
		 * <entry key="84.14" value="5" />
		 */
		pc.add("84.14");
		/*
		 * <entry key="84.13" value="5" />
		 */
		pc.add("84.13");
		/*
		 * <entry key="84.18" value="5" />
		 */
		pc.add("84.18");
		/*
		 * <entry key="84.16" value="5" />
		 */
		pc.add("84.16");
		/*
		 * <entry key="84.04" value="5" />
		 */
		pc.add("84.04");
		/*
		 * <entry key="25900" value="5" />
		 */
		pc.add("25900");
		/*
		 * <entry key="25905" value="5" />
		 */
		pc.add("25905");
		/*
		 * <entry key="25927" value="5" />
		 */
		pc.add("25927");
		/*
		 * <entry key="27290" value="5" />
		 */
		pc.add("27290");
		/*
		 * <entry key="27295" value="5" />
		 */
		pc.add("27295");
		/*
		 * <entry key="27880" value="5" />
		 */
		pc.add("27880");
		/*
		 * <entry key="27882" value="5" />
		 */
		pc.add("27882");
		/*
		 * <entry key="28800" value="5" />
		 */
		pc.add("28800");
		/*
		 * <entry key="28820" value="5" />
		 */
		pc.add("28820");
		/*
		 * <entry key="24900" value="5" />
		 */
		pc.add("24900");
		/*
		 * <entry key="24920" value="5" />
		 */
		pc.add("24920");
		/*
		 * <entry key="28805" value="5" />
		 */
		pc.add("28805");
		/*
		 * <entry key="28810" value="5" />
		 */
		pc.add("28810");
		/*
		 * <entry key="84.09" value="5" />
		 */
		pc.add("84.09");
		/*
		 * <entry key="84.19" value="5" />
		 */
		pc.add("84.19");
		/*
		 * <entry key="24999" value="5" />
		 */
		pc.add("24999");
		/*
		 * <entry key="25920" value="5" />
		 */
		pc.add("25920");
		/*
		 * <entry key="27598" value="5" />
		 */
		pc.add("27598");
		/*
		 * <entry key="23920" value="5" />
		 */
		pc.add("23920");
		/*
		 * <entry key="23900" value="5" />
		 */
		pc.add("23900");
		/*
		 * <entry key="27888" value="5" />
		 */
		pc.add("27888");
		/*
		 * <entry key="27889" value="5" />
		 */
		pc.add("27889");		
		/*
		 * <entry key="84.11" value="5" />
		 */
		pc.add("84.11");		
		/*
		 * <entry key="84.06" value="5" />
		 */
		pc.add("84.06");		
		/*
		 * <entry key="84.05" value="5" />
		 */
		pc.add("84.05");		
		/*
		 * <entry key="84.10" value="5" />
		 */
		pc.add("84.10");		
		/*
		 * <entry key="84.15" value="5" />
		 */
		pc.add("84.15");
		/*
		 * <entry key="84.08" value="5" />
		 */
		pc.add("84.08");

		/*
		 * STD_DIAGNOSISCODE 344.01 Quadriplegia, C1-C4, Complete STD_DESCRIPTORCODE
		 * 1796292 1 QUADRIPLEGIA
		 */
		dc.add(Diagnosis.CODE_QUADRIPLEGIA_C1_C4_COMPLETE.getCode());		
		/* 
		 * STD_DIAGNOSISCODE 344.02 Quadriplegia, C1-C4, Incomplete
		 * STD_DESCRIPTORCODE 1796292 1 QUADRIPLEGIA
		 */
		dc.add(Diagnosis.CODE_QUADRIPLEGIA_C1_C4_INCOMPLETE.getCode());		
		/* STD_DIAGNOSISCODE 344.04 Quadriplegia, C5-C7, Incomplete
		 * STD_DESCRIPTORCODE 1796292 1 QUADRIPLEGIA
		 */
		dc.add(Diagnosis.CODE_QUADRIPLEGIA_C5_C7_INCOMPLETE.getCode());		
		/* STD_DIAGNOSISCODE 344.09 Other Quadriplegia STD_DESCRIPTORCODE 1796292 1
		 * QUADRIPLEGIA
		 */
		dc.add(Diagnosis.CODE_OTHER_QUADRIPLEGIA.getCode());
		/* STD_DIAGNOSISCODE 344.1 Paraplegia STD_DESCRIPTORCODE 1796293 2
		 * PARAPLEGIA
		 */
		dc.add(Diagnosis.CODE_PARAPLEGIA.getCode());		
		/* STD_DIAGNOSISCODE 369.4 Legal Blindness, as defined in U.S.A.
		 * STD_DESCRIPTORCODE 1796295 4 LEGAL BLINDNESS
		 */
		dc.add(Diagnosis.CODE_LEGAL_BLINDNESS.getCode());		
		/* STD_DIAGNOSISCODE 780.03 Persistent Vegetative State STD_DESCRIPTORCODE
		 * 1796294 3 PERSISTENT VEGETATIVE STATE
		 */
		dc.add(Diagnosis.CODE_PERSISTENT_VEGETATIVE_STATE.getCode());		
		/* STD_DIAGNOSISCODE 344 Quadriplegia, Unspecified STD_DESCRIPTORCODE
		 * 1796292 1 QUADRIPLEGIA
		 */
		dc.add(Diagnosis.CODE_QUADRIPLEGIA_UNSPECIFIED.getCode());		
	}
	
	public void testConvertCDOnly() throws Exception {
		MessagingService ms = this.getMessagingService();
		Person onFile = this.buildSimplePerson();
		CatastrophicDisability cd = new CatastrophicDisability(); 
		boolean flag = ms.convertCDOnly(onFile);
		assertFalse(flag);
		CDCondition cond = new CDCondition();
		cond.setCondition(this.getLookupService().getConditionByCode(Condition.CODE_FIM.getCode()));
		cd.addCondition(cond);
		onFile.addClinicalDetermination(cd);
		flag = ms.convertCDOnly(onFile);
		assertTrue(flag);
	}
	
	public void testProcessCDConversionService() throws Exception {
		MessagingService ms = this.getMessagingService();
		Person onFile = this.buildSimplePerson();
		this.attachVerifiedEnrollment(onFile);
		CatastrophicDisability cd = createCatastrophicDisability(
				new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
		onFile.setCatastrophicDisability(cd);

		Set<String> conditions = new HashSet<String>();
		Set<String> procedures = new HashSet<String>();
		Set<String> diagnoses = new HashSet<String>();		
		conditions.add(Condition.CODE_FIM.getCode());
		conditions.add(Condition.CODE_FOLS.getCode());
		conditions.add(Condition.CODE_GAF.getCode());
		conditions.add(Condition.CODE_KATZ.getCode());
		
		procedures.add(Procedure.CODE_TOE_AMPUTATION.getCode());
		
		Person result = processCDConversion(onFile, conditions, procedures, diagnoses);
		
		int conditionCount = countDescriptorsOfType(result, 
				DescriptorType.CODE_DEFICIENCIES_OF_PHYSICAL_OR_MENTAL_FUNCTION.getCode());
		int procedureCount = countDescriptorsOfType(result, 
				DescriptorType.CODE_AMPUTATION_DISARTICULATION_DETACHMENT.getCode());

		assertTrue("actual="+conditionCount, conditionCount == 1);
		assertTrue("actual="+procedureCount,procedureCount == 1);		
		
	}		
	
	public void testAllConversionScenariosSingle() throws Exception {
		MessagingService ms = this.getMessagingService();

		for (String code : cc) {
			Person onFile = this.buildSimplePerson();
			CatastrophicDisability cd = createCatastrophicDisability(
					new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
			onFile.setCatastrophicDisability(cd);
			Person incoming = savePerson(onFile);
			Set<String> ccLocal = new HashSet<String>();
			ccLocal.add(code);
			Set<String> pcLocal = new HashSet<String>();
			Set<String> dcLocal = new HashSet<String>();		
			
			processCDOnTheFly(incoming, ccLocal, pcLocal, dcLocal);
			assertTrue(incoming.getCatastrophicDisability().getConditions().size() == 1);
			assertTrue(incoming.getCatastrophicDisability().getDiagnoses().size() == 0);
			assertTrue(incoming.getCatastrophicDisability().getProcedures().size() == 0);			
			assertTrue(countConditionConversions(incoming) == 1);	
			assertTrue(countProcedureConversions(incoming) == 0);	
			assertTrue(countDiagnosisConversions(incoming) == 0);	
			System.out.println("Verified Condition conversion for code="+code);			
		}
		for (String code : pc) {
			Person onFile = this.buildSimplePerson();
			CatastrophicDisability cd = createCatastrophicDisability(
					new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
			onFile.setCatastrophicDisability(cd);
			Person incoming = savePerson(onFile);
			Set<String> ccLocal = new HashSet<String>();
			Set<String> pcLocal = new HashSet<String>();
			pcLocal.add(code);			
			Set<String> dcLocal = new HashSet<String>();		
			
			processCDOnTheFly(incoming, ccLocal, pcLocal, dcLocal);
			assertTrue(incoming.getCatastrophicDisability().getConditions().size() == 0);
			assertTrue(incoming.getCatastrophicDisability().getDiagnoses().size() == 0);
			assertTrue(incoming.getCatastrophicDisability().getProcedures().size() == 1);			
			assertTrue(countConditionConversions(incoming) == 0);	
			assertTrue(countProcedureConversions(incoming) == 1);	
			assertTrue(countDiagnosisConversions(incoming) == 0);		
			System.out.println("Verified Procedure conversion for code="+code);			
		}
		for (String code : dc) {
			Person onFile = this.buildSimplePerson();
			CatastrophicDisability cd = createCatastrophicDisability(
					new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
			onFile.setCatastrophicDisability(cd);
			Person incoming = savePerson(onFile);
			Set<String> ccLocal = new HashSet<String>();
			Set<String> pcLocal = new HashSet<String>();
			Set<String> dcLocal = new HashSet<String>();		
			dcLocal.add(code);
			
			processCDOnTheFly(incoming, ccLocal, pcLocal, dcLocal);
			assertTrue(incoming.getCatastrophicDisability().getConditions().size() == 0);
			assertTrue(incoming.getCatastrophicDisability().getDiagnoses().size() == 1);
			assertTrue(incoming.getCatastrophicDisability().getProcedures().size() == 0);			
			assertTrue(countConditionConversions(incoming) == 0);	
			assertTrue(countProcedureConversions(incoming) == 0);	
			assertTrue(countDiagnosisConversions(incoming) == 1);		
			System.out.println("Verified Diagnosis conversion for code="+code);
		}		
	}	
	
	public void testConditionsToDecriptorsOnly() throws Exception {
		MessagingService ms = this.getMessagingService();
		Person onFile = this.buildSimplePerson();
		CatastrophicDisability cd = createCatastrophicDisability(
				new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
		onFile.setCatastrophicDisability(cd);
		Person incoming = savePerson(onFile);

		Set<String> pc = new HashSet<String>();
		Set<String> dc = new HashSet<String>();		
		
		processCDOnTheFly(incoming, cc, pc, dc);
		
		int conditionCount = countConditionConversions(incoming);
		assertTrue(conditionCount == 1);
	}
	
	public void testProceduresToDecriptorsOnly() throws Exception {
		MessagingService ms = this.getMessagingService();
		Person onFile = this.buildSimplePerson();
		CatastrophicDisability cd = createCatastrophicDisability(
				new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
		onFile.setCatastrophicDisability(cd);
		Person incoming = savePerson(onFile);

		Set<String> cc = new HashSet<String>();
		Set<String> dc = new HashSet<String>();		
		
		processCDOnTheFly(incoming, cc, pc, dc);
		
		int procedureCount = this.countProcedureConversions(incoming);

		assertTrue(procedureCount == 1);		
	}


	public void testDiagnosesToDecriptorsOnly() throws Exception {
		MessagingService ms = this.getMessagingService();
		Person onFile = this.buildSimplePerson();
		CatastrophicDisability cd = createCatastrophicDisability(
				new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
		onFile.setCatastrophicDisability(cd);
		Person incoming = savePerson(onFile);

		Set<String> cc = new HashSet<String>();
		Set<String> pc = new HashSet<String>();
		
		processCDOnTheFly(incoming, cc, pc, dc);
		
		int diagnosisCount = countDiagnosisConversions(incoming);
		
		assertTrue("epected="+4+" but actual="+diagnosisCount, diagnosisCount == 4); // remember, no multiples of same type		
	}	
	
	private int countDiagnosisConversions(Person incoming) {
		int diagnosisCount = countDescriptorsOfType(incoming, 
				DescriptorType.CODE_QUADRIPLEGIA.getCode());
		diagnosisCount = countDescriptorsOfType(incoming, 
				DescriptorType.CODE_PARAPLEGIA.getCode()) + diagnosisCount;
		diagnosisCount = countDescriptorsOfType(incoming, 
				DescriptorType.CODE_PERSISTENT_VEGETATIVE_STATE.getCode()) + diagnosisCount;
		diagnosisCount = countDescriptorsOfType(incoming, 
				DescriptorType.CODE_LEGAL_BLINDNESS.getCode()) + diagnosisCount;
		return diagnosisCount;
	}
	private int countConditionConversions(Person incoming) {
		int conditionCount = countDescriptorsOfType(incoming, 
				DescriptorType.CODE_DEFICIENCIES_OF_PHYSICAL_OR_MENTAL_FUNCTION.getCode());		
		return conditionCount;
	}	
	private int countProcedureConversions(Person incoming) {
		int procedureCount = countDescriptorsOfType(incoming, 
				DescriptorType.CODE_AMPUTATION_DISARTICULATION_DETACHMENT.getCode());		
		return procedureCount;
	}		
	public void testProceduresToDecriptorsOnlyNegative() throws Exception {
		MessagingService ms = this.getMessagingService();
		Person onFile = this.buildSimplePerson();
		CatastrophicDisability cd = createCatastrophicDisability(
				new Date(), new Date(), CDDeterminationMethod.CODE_AUTOMATED_RECORD_REVIEW.getCode(), "TESTER");
		onFile.setCatastrophicDisability(cd);
		Person incoming = savePerson(onFile);

		Set<String> cc = new HashSet<String>();
		Set<String> pc = new HashSet<String>();
		Set<String> dc = new HashSet<String>();		
		
		pc.add("2117"); // unused code
		
		processCDOnTheFly(incoming, cc, pc, dc);
		
		int procedureCount = countDescriptorsOfType(incoming, 
				DescriptorType.CODE_AMPUTATION_DISARTICULATION_DETACHMENT.getCode());

		assertTrue(procedureCount == 0);		
	}			
	
	private int countDescriptorsOfType(Person person, String type) {
		int count = 0;
		for (CDDescriptor cdDescriptor : person.getCatastrophicDisability().getCDDescriptors()) {
			 if (cdDescriptor.getDescriptorType().getCode().equals(type)) {
				 count++;
			 }
		}	
		return count; 
	}
	
	private void processCDOnTheFly(Person person, 
			Set<String> conditionCodes,
			Set<String> procedureCodes,
			Set<String> diagnosisCodes) throws Exception {
		MessagingService ms = this.getMessagingService();
		CatastrophicDisability cd = person.getCatastrophicDisability();
		
		for (String code : conditionCodes) {
			Condition type = this.getLookupService().getConditionByCode(code);
			CDCondition c = new CDCondition(); 
			c.setCondition(type);
			cd.addCondition(c);
		}
		for (String code : diagnosisCodes) {
			Diagnosis type = this.getLookupService().getDiagnosisByCode(code);
			CDDiagnosis d = new CDDiagnosis();
			d.setDiagnosis(type);
			cd.addDiagnosis(d);
		}
		for (String code : procedureCodes) {	
			Procedure type = this.getLookupService().getProcedureByCode(code);
			CDProcedure p = new CDProcedure();
			p.setProcedure(type);
			cd.addProcedure(p);
		}		

		boolean flag = ms.convertCDOnly(person);
	}
	
	private Person processCDConversion(Person person, 
			Set<String> conditionCodes,
			Set<String> procedureCodes,
			Set<String> diagnosisCodes) throws Exception {
		MessagingService ms = this.getMessagingService();
		CatastrophicDisability cd = person.getCatastrophicDisability();

		
		for (String code : conditionCodes) {
			Condition type = this.getLookupService().getConditionByCode(code);
			CDCondition c = new CDCondition(); 
			c.setCondition(type);
			cd.addCondition(c);
		}
		for (String code : diagnosisCodes) {
			Diagnosis type = this.getLookupService().getDiagnosisByCode(code);
			CDDiagnosis d = new CDDiagnosis();
			d.setDiagnosis(type);
			cd.addDiagnosis(d);
		}
		for (String code : procedureCodes) {	
			Procedure type = this.getLookupService().getProcedureByCode(code);
			CDProcedure p = new CDProcedure();
			p.setProcedure(type);
			cd.addProcedure(p);
		}		
		Person incoming = savePerson(person);
		ms.processCDConversion(incoming);
		// we have to get the updated person from cache since person is not returned
		Person result = this.getPerson(incoming.getEntityKey().getKeyValueAsString());
		
		return result;
	}		
	
	public void testCD() throws Exception {
		// This test was just to point out to myself that although there should only be ONE instance
		// of CD on person, it is possible to circumvent this by addClinicalDetermination. Don't want to do this.
		Person onFile = this.buildSimplePerson();
		CatastrophicDisability cd1 = new CatastrophicDisability();
		cd1.setCatastrophicallyDisabled(Boolean.TRUE);
		CatastrophicDisability cd2 = new CatastrophicDisability();
		cd2.setCatastrophicallyDisabled(Boolean.FALSE);
		onFile.addClinicalDetermination(cd1);
		onFile.addClinicalDetermination(cd2);		
		assertTrue(onFile.getClinicalDeterminations().size() == 2);
		assertFalse(onFile.getCatastrophicDisability().getCatastrophicallyDisabled().booleanValue());
	}
			
	
	public void testDescriptorTypeLookup() throws Exception {
		MessagingService ms = this.getMessagingService();
		Person onFile = this.buildSimplePerson();
		CatastrophicDisability cd = new CatastrophicDisability();
		DescriptorType d = this.getLookupService().getDescriptorTypeByCode(
				DescriptorType.CODE_AMPUTATION_DISARTICULATION_DETACHMENT.getCode());
		assertNotNull(d);

	}
}
