
package gov.va.med.cds.testharness.vhim400.builders;

import gov.va.med.cds.clinicaldata.DomainEntryPoint;
import gov.va.med.cds.clinicaldata.vhim400.PathwaysDataResponse400;
import gov.va.med.cds.uniqueidentifier.UniqueIdentifier;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.collections4.map.MultiValueMap;
import org.dom4j.Document;
import org.dom4j.Element;

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

public class PathwaysDataBuilder extends PathwaysDataResponse400 implements ClinicalDataBuilderInterface {
	private static final String CLIENT_NAME_ELEMENT_NAME = "clientName";
	private static final String CLIENT_REQUEST_INITIATION_TIME_ELEMENT_NAME = "clientRequestInitiationTime";

	private Map<String, ClinicalDomainBuilderFactoryInterface> domainEntryPointsWrappersMap = null;
	private Map<String, DomainEntryPoint> domainEntryPointsMap = new HashMap<String, DomainEntryPoint>();

	public PathwaysDataBuilder() {

	}

	public PathwaysDataBuilder( String patientElementPath, String clinicalRootPath, String errorResponseTemplateId,
            String schemaNamespacePrefix, String schemaNamespaceUri, String schemaLocalName )
	{
		super( patientElementPath, clinicalRootPath, errorResponseTemplateId, schemaNamespacePrefix, schemaNamespaceUri, schemaLocalName );
	}

	public Document createClinicalData(String templateId) {
		return createClinicalData(templateId, 1);

	}

	public Document createClinicalData(Set<DomainEntryPoint> domainEntryPointsSet) {
		return createClinicalData(domainEntryPointsSet, 1);
	}

	public Document createClinicalData(String templateId, MultiValueMap map, String facilityId, String menuOption, String requestId, String clName, String clReqTime) {
		Document clinicalData = buildEmptyPathwaysDocumentWithPatient(templateId, requestId, clName, clReqTime );
		buildClinicalDataFromTemplateId(templateId, clinicalData, map, facilityId, menuOption);

		return clinicalData;
	}

	protected void buildClinicalDataFromTemplateId(String templateId, Document clinicalData, MultiValueMap map,
			String facilityId, String menuOption) {
		// get domain entry points from the template
		Set<DomainEntryPoint> templateDomainEntryPointsSet = null;
		if ((templateId != null) && (!templateId.equals(""))) {
			templateDomainEntryPointsSet = getDomainEntryPoints(templateId);
		}
		for (gov.va.med.cds.clinicaldata.DomainEntryPoint domainEntryPoint : templateDomainEntryPointsSet) {
			buildClinicalDataFromEntryPoint(domainEntryPoint.name(), clinicalData, map, facilityId, menuOption);
		}
	}

	private void buildClinicalDataFromEntryPoint(String domainEntryPoint, Document clinicalData, MultiValueMap map,
			String facilityId, String menuOption) {
		if (domainEntryPoint.equals("PatientDataAccessedEvent")) {
			SensitivePatientDataAccessBuilderFactoryInterface sensitivePatientDataAccessBuilderFactory = null;
			sensitivePatientDataAccessBuilderFactory = (SensitivePatientDataAccessBuilderFactoryInterface) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			sensitivePatientDataAccessBuilderFactory.createClinicalDomainData(clinicalData, map, facilityId,
					menuOption);
		}
	}

	public Document createClinicalData(String templateId, MultiValueMap map, String facilityId1, String facilityId2, String menuOption, String requestId, String clName, String clReqTime) {
		Document clinicalData = buildEmptyPathwaysDocumentWithPatient(templateId, requestId, clName, clReqTime );
		buildClinicalDataFromTemplateId(templateId, clinicalData, map, facilityId1, facilityId2, menuOption);

		return clinicalData;
	}

	protected void buildClinicalDataFromTemplateId(String templateId, Document clinicalData, MultiValueMap map,
			 String facilityId1, String facilityId2, String menuOption) {
		// get domain entry points from the template
		Set<DomainEntryPoint> templateDomainEntryPointsSet = null;
		if ((templateId != null) && (!templateId.equals(""))) {
			templateDomainEntryPointsSet = getDomainEntryPoints(templateId);
		}
		for (gov.va.med.cds.clinicaldata.DomainEntryPoint domainEntryPoint : templateDomainEntryPointsSet) {
			buildClinicalDataFromEntryPoint(domainEntryPoint.name(), clinicalData, map, facilityId1, facilityId2, menuOption);
		}
	}

	private void buildClinicalDataFromEntryPoint(String domainEntryPoint, Document clinicalData, MultiValueMap map,
			 String facilityId1, String facilityId2, String menuOption) {
		if (domainEntryPoint.equals("PatientDataAccessedEvent")) {
			SensitivePatientDataAccessBuilderFactoryInterface sensitivePatientDataAccessBuilderFactory = null;
			sensitivePatientDataAccessBuilderFactory = (SensitivePatientDataAccessBuilderFactoryInterface) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			sensitivePatientDataAccessBuilderFactory.createClinicalDomainData(clinicalData, map, facilityId1, facilityId2,
					menuOption);
		}
	}

	public Document createClinicalData(String templateId, int count) {
		// create clinical data with every thing except clinical domain data
		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier();
		buildClinicalDataFromTemplateId(templateId, count, clinicalData, patientIdentifier, null);

		return clinicalData;
	}

	public Document createClinicalData(Set<DomainEntryPoint> domainEntryPointsSet, int count) {
		Document clinicalData = buildClinicalData();
		Element patientIdentifier = createPatientIdentifier();
		buildClinicalDataFromEntryPoints(domainEntryPointsSet, count, clinicalData, patientIdentifier);

		return clinicalData;
	}

	public Document createClinicalData(String templateId, String entityId) {
		return createClinicalData(templateId, 1, entityId);
	}

	public Document createClinicalData(Set<DomainEntryPoint> domainEntryPointsSet, String entityId) {
		return createClinicalData(domainEntryPointsSet, 1, entityId);
	}

	public Document createClinicalData(String templateId, int count, String entityId) {
		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(entityId);
		buildClinicalDataFromTemplateId(templateId, count, clinicalData, patientIdentifier, null);

		return clinicalData;
	}

	public Document createClinicalData(Set<DomainEntryPoint> domainEntryPointsSet, int count, String entityId) {
		Document clinicalData = buildClinicalData();
		Element patientIdentifier = createPatientIdentifier(entityId);
		buildClinicalDataFromEntryPoints(domainEntryPointsSet, count, clinicalData, patientIdentifier);

		return clinicalData;
	}

	public Document createClinicalData(String templateId, int count, Element patient) {

		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromTemplateId(templateId, count, clinicalData, patientIdentifier, null);

		return clinicalData;
	}

	public Document createClinicalData(String templateId, int count, Element patient, String skillType) {

		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromTemplateId(templateId, count, clinicalData, patientIdentifier, skillType);

		return clinicalData;
	}

	/**
	 * Method is used when multiple domainEntryPoints share the same Create
	 * TemplateId - a substitution key is required to locate the correct builder
	 * for the domain being created.
	 * 
	 * @param templateId
	 * @param templateSubstitutionKey
	 * @param count
	 * @param patient
	 * @return
	 */
	public Document createClinicalData(String templateId, String templateSubstitutionKey, int count, Element patient) {
		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromTemplateId(templateSubstitutionKey, count, clinicalData, patientIdentifier, null);

		return clinicalData;
	}

	public Document createClinicalData(Set<DomainEntryPoint> domainEntryPointsSet, int count, Element patient) {
		Document clinicalData = buildClinicalData();
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromEntryPoints(domainEntryPointsSet, count, clinicalData, patientIdentifier);

		return clinicalData;
	}

	public Document createClinicalData(DomainEntryPoint domainEntryPoint, int count, Element patient) {
		Document clinicalData = buildClinicalData();
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromEntryPoint(domainEntryPoint.getName(), count, clinicalData, patientIdentifier, null);

		return clinicalData;
	}

	public Document createClinicalData(DomainEntryPoint domainEntryPoint, int count) {
		Element patientIdentifier = createPatientIdentifier();
		return createClinicalData(domainEntryPoint, count, patientIdentifier);

	}

	public Document createClinicalData(String templateId, Element clinicalDataPatient) {
		return createClinicalData(templateId, 1, clinicalDataPatient);
	}

	public Document createClinicalData(Set<DomainEntryPoint> domainEntryPointsSet, Element clinicalDataPatient) {
		return createClinicalData(domainEntryPointsSet, 1, clinicalDataPatient);
	}

	public Document updateClinicalData(String templateId, int count, Element patient) {
		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromTemplateId(templateId, count, clinicalData, patientIdentifier, null);

		return clinicalData;
	}

	public Document deleteClinicalData(DomainEntryPoint domainEntryPoint, String templateId, Element patient,
			String recordIdentifier, String recordStatus) {
		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(patient);
		buildDeleteClinicalDataFromEntryPoint(domainEntryPoint.getName(), clinicalData, recordIdentifier, recordStatus);

		return clinicalData;
	}

	public Document deleteClinicalDataWithRecordVersion(DomainEntryPoint domainEntryPoint, String templateId,
			String recordIdentifier, String recordVersion) {
		Document clinicalData = buildClinicalData(templateId);
		// Element patientIdentifier = createPatientIdentifier( patient );
		buildDeleteClinicalDataFromEntryPointWithRecordVersion(domainEntryPoint.getName(), clinicalData,
				recordIdentifier, recordVersion);

		return clinicalData;
	}

	public Document deleteClinicalData(DomainEntryPoint domainEntryPoint, String templateId, String recordIdentifier,
			String recordStatus) {
		Document clinicalData = buildClinicalData(templateId);
		buildDeleteClinicalDataFromEntryPoint(domainEntryPoint.getName(), clinicalData, recordIdentifier, recordStatus);

		return clinicalData;
	}

	private Element createPatientIdentifier() {

		return BaseDatatypesBuilder.createHL72PersonIdentifier("Identifier");
	}

	protected Element createPatientIdentifier(Element patientIdentifier) {

		Element patient = null;
		if (patientIdentifier.elements("identifer") == null) {
			// The element patientIdentifier is a ClinicalDataPatient with an
			// empty identifier...create a new patientIdentifier
			patient = BaseDatatypesBuilder.createHL72PersonIdentifier("Identifier");
		} else {
			patient = patientIdentifier;
		}
		return patient;
	}

	private Element createPatientIdentifier(String patientId) {

		return BaseDatatypesBuilder.createHL72PersonIdentifier("Identifier", patientId);
	}

	/**
	 * For a given template's domain entry point, iterate thru the static/spring
	 * maintained map to call the ClinicalDomainBuilderFactoryInterface wrapper
	 * to build the clinical domain data. Handles calls that pass aTemplateId
	 */
	protected void buildClinicalDataFromTemplateId(String templateId, int aCount, Document clinicalData,
			Element patientIdentifier, String skillType) {
		// get domain entry points from the template
		Set<DomainEntryPoint> templateDomainEntryPointsSet = null;
		if ((templateId != null) && (!templateId.equals(""))) {
			templateDomainEntryPointsSet = getDomainEntryPoints(templateId);
		}

		for (gov.va.med.cds.clinicaldata.DomainEntryPoint domainEntryPoint : templateDomainEntryPointsSet) {
			buildClinicalDataFromEntryPoint(domainEntryPoint.name(), aCount, clinicalData, patientIdentifier,
					skillType);
		}
	}

	protected void buildClinicalDataFromTemplateId(String templateId, int aCount, Document clinicalData,
			Element patientIdentifier, String goalName, String entryDate) {
		// get domain entry points from the template
		Set<DomainEntryPoint> templateDomainEntryPointsSet = null;
		if ((templateId != null) && (!templateId.equals(""))) {
			templateDomainEntryPointsSet = getDomainEntryPoints(templateId);
		}

		for (gov.va.med.cds.clinicaldata.DomainEntryPoint domainEntryPoint : templateDomainEntryPointsSet) {
			buildClinicalDataFromEntryPoint(domainEntryPoint.name(), aCount, clinicalData, patientIdentifier, goalName,
					entryDate);
		}
	}

	/**
	 * For a given template's domain entry point, iterate through the
	 * static/spring maintained map to call the
	 * ClinicalDomainBuilderFactoryInterface wrapper to build the clinical
	 * domain data. Handles calls that pass domainEntryPointsSet
	 */
	private void buildClinicalDataFromEntryPoints(Set<DomainEntryPoint> domainEntryPointsSet, int count,
			Document clinicalData, Element patientIdentifier) {
		for (DomainEntryPoint domainEntryPoint : domainEntryPointsSet) {
			buildClinicalDataFromEntryPoint(domainEntryPoint.getName(), count, clinicalData, patientIdentifier, null);
		}
	}

	/**
	 * For a given template's domain entry point, iterate through the
	 * static/spring maintained map to call the
	 * ClinicalDomainBuilderFactoryInterface wrapper to build the clinical
	 * domain data. Handles calls that pass aTemplateId/domainEntryPointsSet
	 */
	private void buildClinicalDataFromEntryPoint(String domainEntryPoint, int count, Document clinicalData,
			Element patientIdentifier, String skillType) {

		if (domainEntryPoint.equals("SkillTracking")) {
			HATrackingClinicalDomainBuilderFactoryInterface haTrackingClinicalDomainBuilderFactory = null;
			haTrackingClinicalDomainBuilderFactory = (HATrackingClinicalDomainBuilderFactoryInterface) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			haTrackingClinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, count, patientIdentifier,
					skillType);
		} else {
			ClinicalDomainBuilderFactoryInterface clinicalDomainBuilderFactory = null;
			clinicalDomainBuilderFactory = (ClinicalDomainBuilderFactoryInterface) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			clinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, count, patientIdentifier);
		}

	}

	private void buildClinicalDataFromEntryPoint(String domainEntryPoint, int count, Document clinicalData,
			Element patientIdentifier, String goalName, String entryDate) {
		if (domainEntryPoint.equals("GoalTracking")) {
			HATrackingClinicalDomainBuilderFactoryInterface haTrackingClinicalDomainBuilderFactory = null;
			haTrackingClinicalDomainBuilderFactory = (HATrackingClinicalDomainBuilderFactoryInterface) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			haTrackingClinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, count, patientIdentifier,
					goalName, entryDate);
		} else {
			ClinicalDomainBuilderFactoryInterface clinicalDomainBuilderFactory = null;
			clinicalDomainBuilderFactory = (ClinicalDomainBuilderFactoryInterface) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			clinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, count, patientIdentifier);
		}
	}

	private void buildDeleteClinicalDataFromEntryPoint(String domainEntryPoint, Document clinicalData,
			String recordIdentifier, String recordStatus) {
		if (domainEntryPoint.equals("IntoleranceCondition")) {
			IntoleranceConditionBuilderFactory clinicalDomainBuilderFactory = (IntoleranceConditionBuilderFactory) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			clinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, recordIdentifier);
		} else if (domainEntryPoint.equals("NonVaMedicationEvent")) {
			NonVaMedBuilderFactory clinicalDomainBuilderFactory = (NonVaMedBuilderFactory) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			clinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, recordIdentifier, recordStatus);
		} else {
			HAClinicalDomainBuilderFactoryInterface clinicalDomainBuilderFactory = (HAClinicalDomainBuilderFactoryInterface) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			clinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, recordIdentifier, recordStatus);
		}

	}

	private void buildDeleteClinicalDataFromEntryPointWithRecordVersion(String domainEntryPoint, Document clinicalData,
			String recordIdentifier, String recordVersion) {
		if (domainEntryPoint.equals("IntoleranceCondition")) {
			IntoleranceConditionBuilderFactory clinicalDomainBuilderFactory = (IntoleranceConditionBuilderFactory) domainEntryPointsWrappersMap
					.get(domainEntryPoint);
			clinicalDomainBuilderFactory.createClinicalDomainData(clinicalData, recordIdentifier, recordVersion);
		}

	}

	public ClinicalDomainBuilderFactoryInterface getClinicalDomainBuildFactory(String domainEntryPoint) {
		ClinicalDomainBuilderFactoryInterface clinicalDomainBuilderFactory = null;
		clinicalDomainBuilderFactory = (ClinicalDomainBuilderFactoryInterface) domainEntryPointsWrappersMap
				.get(domainEntryPoint);
		return clinicalDomainBuilderFactory;
	}

	protected Document buildClinicalData(String templateId) {
		Element patient = CommonBuilder.createClinicalDataPatient("patient");

		return buildClinicalDataDocument(templateId, patient);
	}

	private Document buildClinicalData() {
		Element patient = CommonBuilder.createClinicalDataPatient("patient");

		return buildClinicalDataDocument(null, patient);
	}

	private Document buildClinicalDataDocument(String templateId, Element clinicalDataPatient) {
		Document clinicalDataDocument = null;

		if (templateId.equals("IntoleranceConditionCreateOrUpdate40060")
				|| templateId.equals("AllergyAssessmentCreateOrUpdate40060")
				|| templateId.equals("NonVAMedicationCreateOrUpdate3") || templateId.equals("NonVAMedicationDelete1")) {
			clinicalDataDocument = buildEmptyClinicalDocument(templateId, null, "ClientName");
		} else if (templateId.equals("HealthAdapterCreate1") || templateId.equals("HealthAdapterUpdate1")
				|| templateId.equals("HealthAdapterDelete1") || templateId.startsWith("DBQ")
				|| templateId.equals("IntoleranceConditionDelete40030")
				|| templateId.equals("PharmacyCreateOrUpdate40060") || templateId.equals("LabCreateOrUpdate40060")) {
			clinicalDataDocument = buildEmptyClinicalDocument(templateId, null);
		} else {
			clinicalDataDocument = buildEmptyClinicalDocument(templateId, new UniqueIdentifier().toString());
		}
		return clinicalDataDocument;
	}

	public Map<String, ClinicalDomainBuilderFactoryInterface> getDomainEntryPointsWrappersMap() {
		return domainEntryPointsWrappersMap;
	}

	public void setDomainEntryPointsWrappersMap(
			Map<String, ClinicalDomainBuilderFactoryInterface> domainEntryPointsWrappersMap) {
		this.domainEntryPointsWrappersMap = domainEntryPointsWrappersMap;
	}

	public Set<DomainEntryPoint> getDomainEntryPoints(String templateId) {
		Set<DomainEntryPoint> domainEntryPointSet = new HashSet<DomainEntryPoint>();
		domainEntryPointSet.add(domainEntryPointsMap.get(templateId));
		return domainEntryPointSet;
	}

	public void setDomainEntryPointsMap(Map<String, DomainEntryPoint> domainEntryPointsMap) {

		this.domainEntryPointsMap = domainEntryPointsMap;
	}

	@Override
	public void addAuditInfo(Element clinicalDataElement, String clientName) {
		if (clientName != null) {
			getChildElement(clinicalDataElement, CLIENT_NAME_ELEMENT_NAME, true).addText(clientName);
			getChildElement(clinicalDataElement, CLIENT_REQUEST_INITIATION_TIME_ELEMENT_NAME, true)
					.addText(DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(new Date()));
		}
	}

	public Document createClinicalData(String templateId, String templateSubstitutionKey, int count, Element patient,
			String skillType) {
		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromTemplateId(templateSubstitutionKey, count, clinicalData, patientIdentifier, skillType);

		return clinicalData;
	}

	public Document createClinicalData(String templateId, String templateSubstitutionKey, int count, Element patient,
			String goalName, String entryDate) {
		Document clinicalData = buildClinicalData(templateId);
		Element patientIdentifier = createPatientIdentifier(patient);
		buildClinicalDataFromTemplateId(templateSubstitutionKey, count, clinicalData, patientIdentifier, goalName,
				entryDate);

		return clinicalData;
	}
    
 //TODO: REMOVE   
    public Document createClinicalData( String templateId, MultiValueMap map, String facilityId, String menuOption, String requestId )
    {
    	return null;
    }
    
    public Document createClinicalData( String templateId, MultiValueMap map, String facilityId1, String facilityId2, String menuOption, String requestId )
    {
    	return null;
 
    }
}
