

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


import gov.va.med.cds.clinicaldata.DomainEntryPoint;
import gov.va.med.cds.clinicaldata.vhim400.ClinicalDataResponse400;
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 ClinicalDataBuilder
    extends
        ClinicalDataResponse400
    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 ClinicalDataBuilder( )
    {

    }


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

    }


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

    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;
    }

}
