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

//Java classes
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;

import gov.va.med.fw.rule.RuleParameters;
import gov.va.med.fw.service.ServiceException;

import gov.va.med.esr.common.model.ee.CDCondition;
import gov.va.med.esr.common.model.ee.CDDiagnosis;
import gov.va.med.esr.common.model.ee.CatastrophicDisability;
import gov.va.med.esr.common.model.ee.ClinicalDetermination;
import gov.va.med.esr.common.model.ee.NoseThroatRadium;
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.VAFacility;
import gov.va.med.esr.common.model.person.Person;
import gov.va.med.esr.common.rule.data.ClinicalDeterminationInputData;
import gov.va.med.esr.common.util.RuleAbstractTestCase;


/**
 * @author VHAIVMNgVS
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public class ClinicalValidationServiceTest extends RuleAbstractTestCase {

    public ClinicalValidationServiceTest(String testName) throws Exception {
        super(testName);
    }

    private NoseThroatRadium createNoseThroatRadium() throws Exception {
        return createNoseThroatRadium(new Date(), VAFacility.CODE_CONNECTICUT_HCS, "M");
    }

    public void testIsDiagnosisTypeEqual() throws Exception {
        Person person = this.buildTestPerson();
        addDiagnosisToCatastrophicDisability("ICD-D", person);

        try {
            this.validateClinicalDetermination(person);
        } catch (Exception ex) {
            String errorMessage = ex.getMessage();
            assertEquals(errorMessage,
                    "the correct diagnosis type are not included");
        }
    }

    public void testIsNTRVerificationDateNotPrecise() throws Exception {
        // Add in the diagnosis

        Person person = this.buildTestPerson();
        updateDiagnosisDisability("ICD-9", person);
        person.addClinicalDetermination(this.createNoseThroatRadium());
        try {
            this.validateClinicalDetermination(person);
        } catch (Exception ex) {
            String errorMessage = ex.getMessage();
            assertEquals(errorMessage, "verification date must be precise");
        }
    }

    public void testIsReviewDateGreaterThanDateofDecision() throws Exception {
        Person person = this.buildTestPerson();
        updateReviewDateLaterThanReasonDate(person);
        try {
            this.validateClinicalDetermination(person);
        } catch (Exception ex) {
            String errorMessage = ex.getMessage();
            assertEquals(errorMessage,
                    "review date can not be greater than date of decision");
        }
    }

    public void testIsScoreNotProvided() throws Exception {
        Person person = this.buildTestPerson();
        addConditionWithNullScore(person);
        // this will make sure that testisReviewDateGreaterThanDateofDecision
        // won't throw exception
        updateReviewDateEarlierThanReasonDate(person);

        try {
            this.validateClinicalDetermination(person);
        } catch (Exception ex) {
            String errorMessage = ex.getMessage();
            assertEquals(errorMessage, "score must be provided");
        }
    }

    public void testIsSiteDetermingMSTNotProvided() throws Exception {
        Person person = this.buildTestPerson();
        person.addClinicalDetermination(createMilitarySexualTrauma("Y"));
        //  this will ensure that executeisScoreNotProvided()won't throw
        // exception
        addConditionScore(person);
        try {
            this.validateClinicalDetermination(person);
            fail("Site Determining MST Status is required if MST Status is Y");
        } catch (Exception ex) {
            String errorMessage = ex.getMessage();
            assertEquals(errorMessage,
                    "Site Determining MST Status is required if MST Status is Y");
        }
    }

    public void testIsSiteDetermingMSTNotRequired() throws Exception {
        Person person = this.buildTestPerson();
        person.addClinicalDetermination(createMilitarySexualTrauma("U"));

        this.validateClinicalDetermination(person);
    }

    private void validateClinicalDetermination(Person person) throws Exception {
        Set incomingClinicalDeterminations = (person != null) ? person
                .getClinicalDeterminations() : null;
        if (incomingClinicalDeterminations != null) {
            Iterator iter = incomingClinicalDeterminations.iterator();
            while (iter.hasNext()) {
                ClinicalDetermination clinicalDetermination = (ClinicalDetermination) iter
                        .next();
                this.executeClinicalValidationRule(clinicalDetermination);
            }
        }
    }

    private void executeClinicalValidationRule(ClinicalDetermination clinicalDetermination) throws Exception {
        ClinicalDeterminationInputData data = new ClinicalDeterminationInputData(clinicalDetermination);
        this.getRuleFlowService().execute(getClinicalValidationRuleParameters(), data);
    }

    /**
     * @return Returns the clinicalValidationRuleParameters.
     */
    private RuleParameters getClinicalValidationRuleParameters()
            throws ServiceException {
        return (RuleParameters) applicationContext.getBean("clinicalValidationRuleParameters");
    }

    private Person buildTestPerson() throws Exception {
        Person person = buildPerson();
        Calendar calendar = Calendar.getInstance();
        calendar.set(1985, 9, 15);
        Date reviewDate = calendar.getTime();
        calendar.set(1989, 7, 25);
        Date dateDecision = calendar.getTime();
        person.addClinicalDetermination(this.createCatastrophicDisability(
                reviewDate, dateDecision, "01", "test reason"));
        return person;
    }

    private void addConditionWithNullScore(Person person) throws Exception {
        CatastrophicDisability catastrophicDisability = this
                .getCatastrophicDisability(person);
        if (catastrophicDisability == null)
            return;
        CDCondition cdCondition = new CDCondition();
        cdCondition.setCondition((Condition) this.getLookupService()
                .getConditionByCode("01"));
        catastrophicDisability.addCondition(cdCondition);
    }

    private void updateReviewDateEarlierThanReasonDate(Person person) {
        // Review date is earlier than decision date
        CatastrophicDisability catastrophicDisability = this
                .getCatastrophicDisability(person);
        catastrophicDisability.setReviewDate(getDate(1984, 7, 25));
    }

    private void addDiagnosisToCatastrophicDisability(String diagnosisType,
            Person person) throws Exception {
        CatastrophicDisability catastrophicDisability = this
                .getCatastrophicDisability(person);
        if (catastrophicDisability == null)
            return;
        CDDiagnosis cdDiagnosis = new CDDiagnosis();
        Diagnosis diagnosis = (Diagnosis) this.getLookupService()
                .getDiagnosisByCode(diagnosisType);
        cdDiagnosis.setDiagnosis(diagnosis);
        catastrophicDisability.addDiagnosis(cdDiagnosis);
    }

    private void addConditionScore(Person person) throws Exception {

        CatastrophicDisability catastrophicDisability = this
                .getCatastrophicDisability(person);
        if (catastrophicDisability == null)
            return;
        catastrophicDisability.getConditions().clear();
        CDCondition cdCondition = new CDCondition();
        cdCondition.setCondition((Condition) this.getLookupService()
                .getConditionByCode("01"));
        cdCondition.setScore(new Integer(10));
        catastrophicDisability.addCondition(cdCondition);
    }

    private void updateReviewDateLaterThanReasonDate(Person person) {
        CatastrophicDisability catastrophicDisability = this
                .getCatastrophicDisability(person);
        if (catastrophicDisability == null)
            return;
        catastrophicDisability.setReviewDate(getDate(1989, 7, 25));

    }

    private CatastrophicDisability getCatastrophicDisability(Person person) {
        return (CatastrophicDisability) this.getPersonHelperService()
                .getClinicalDetermination(CatastrophicDisability.class, person);
    }

    private void updateDiagnosisDisability(String diagnosisType, Person person)
            throws Exception {
        CatastrophicDisability catastrophicDisability = this
                .getCatastrophicDisability(person);
        if (catastrophicDisability == null)
            return;
        catastrophicDisability.getDiagnoses().clear();
        addDiagnosisToCatastrophicDisability(diagnosisType, person);
    }
}