package gov.va.med.cds.persistence.hibernate;


import gov.va.med.cds.clinicaldata.DomainEntryPoint;
import gov.va.med.cds.clinicaldata.vhim400.ClinicalDataResponseInterface;
import gov.va.med.cds.filter.EntryFilterInterface;
import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.junit.runners.SuiteAwareRunner;
import gov.va.med.cds.persistence.QueryAssociationInterface;
import gov.va.med.cds.persistence.QueryWorkInterface;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.easymock.EasyMock;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;
import java.util.List;


/**
 * @author mtalbot
 *
 * Test class for Unit Testing the logic contained in the <code>LabHibernateDomModelAssembler</code>
 * class.
 *
 */
@RunWith( SuiteAwareRunner.class )
public class LabHibernateDomModelAssemblerTest
{
    @Test
    @Suite( groups = { "checkintest" } )
    public void testAssembleModelSingleWork( )
        throws Exception
    {
        List<Element> resultsList = new ArrayList<Element>();
        List<Element> resultIdList = new ArrayList<Element>();

        // initialize mocks for test
        ClinicalDataResponseInterface clinicalDataResponse = EasyMock.createMock( ClinicalDataResponseInterface.class );
        EntryFilterInterface entryFilter = EasyMock.createMock( EntryFilterInterface.class );
        QueryAssociationInterface queryAssociation = EasyMock.createMock( QueryAssociationInterface.class );
        QueryWorkInterface queryWork = EasyMock.createMock( QueryWorkInterface.class );
        Document clinicalDataDocument = EasyMock.createMock( Document.class );
        Element clinicalDataElement = EasyMock.createMock( Element.class );
        //Element clinicalDataPatientElement = EasyMock.createMock( Element.class );
        Element clinicalDataPatientElement = EasyMock.createNiceMock( Element.class );
        Element queryWorkResultsElement = EasyMock.createMock( Element.class );

        Element result1 = EasyMock.createMock( Element.class );
        Element result2 = EasyMock.createMock( Element.class );
        Element result3 = EasyMock.createMock( Element.class );
        resultsList.add( result1 );
        resultsList.add( result2 );
        resultsList.add( result3 );

        Element result1Id = EasyMock.createMock( Element.class );
        Element result2Id = EasyMock.createMock( Element.class );
        Element result3Id = EasyMock.createMock( Element.class );
        resultIdList.add( result1Id );
        resultIdList.add( result2Id );
        resultIdList.add( result3Id );

        // set the expectations on the mocks.
        EasyMock.expect( entryFilter.getTemplateId() ).andReturn( "templateId" );
        EasyMock.expect( entryFilter.getRequestId() ).andReturn( "requestId" );
        EasyMock.expect( clinicalDataResponse.buildEmptyClinicalDocumentWithPatient( "templateId", "requestId" ) ).andReturn( clinicalDataDocument );
        EasyMock.expect( clinicalDataResponse.getClinicalRoot( clinicalDataDocument )).andReturn( clinicalDataPatientElement );
        EasyMock.expect( clinicalDataPatientElement.getUniquePath()).andReturn( "//patient" ).anyTimes();
        
        // patient level processing
        EasyMock.expect( queryWork.getQueryAssociation() ).andReturn( queryAssociation );
        EasyMock.expect( queryAssociation.getParentKey() ).andReturn( null );
        EasyMock.expect( queryWork.getResults() ).andReturn( queryWorkResultsElement );
        EasyMock.expect( queryWorkResultsElement.hasContent() ).andReturn( Boolean.TRUE );
        EasyMock.expect( queryWorkResultsElement.elements() ).andReturn( resultsList );

        List clinicalDataPatientElementList = new ArrayList();
        clinicalDataPatientElementList.add( clinicalDataPatientElement );

        // reslult1 processing
//        EasyMock.expect( queryAssociation.getAssociationParent() ).andReturn( "patient" );
        EasyMock.expect( clinicalDataPatientElement.selectNodes( "//patient" ) ).andReturn( clinicalDataPatientElementList );
        EasyMock.expect( queryAssociation.isCollapsable() ).andReturn( false );
        EasyMock.expect( result1.detach() ).andReturn( result1 );
        clinicalDataPatientElement.add( ( Element )result1 );

        // result2 processing
//        EasyMock.expect( queryAssociation.getAssociationParent() ).andReturn( "patient" );
        EasyMock.expect( clinicalDataPatientElement.selectNodes( "//patient" ) ).andReturn( clinicalDataPatientElementList );
        EasyMock.expect( queryAssociation.isCollapsable() ).andReturn( false );
        EasyMock.expect( result2.detach() ).andReturn( result2 );
        clinicalDataPatientElement.add( (Element)result2 );
        
        // result3 processing
//        EasyMock.expect( queryAssociation.getAssociationParent() ).andReturn( "patient" );
        EasyMock.expect( clinicalDataPatientElement.selectNodes( "//patient" ) ).andReturn( clinicalDataPatientElementList );
        EasyMock.expect( queryAssociation.isCollapsable() ).andReturn( false );
        EasyMock.expect( result3.detach() ).andReturn( result3 );
        clinicalDataPatientElement.add( ( Element )result3 );

        // id removal processing
        EasyMock.expect( clinicalDataPatientElement.selectNodes( "//id" ) ).andReturn( resultIdList );
        EasyMock.expect( result1Id.detach() ).andReturn( ( Node )result1Id );
        EasyMock.expect( result2Id.detach() ).andReturn( ( Node )result2Id );
        EasyMock.expect( result3Id.detach() ).andReturn( ( Node )result3Id );

        // get ready to execute the test.
        EasyMock.replay( clinicalDataResponse, entryFilter, queryAssociation, queryWork, clinicalDataDocument, clinicalDataElement,
                        clinicalDataPatientElement, queryWorkResultsElement, result1, result2, result3, result1Id, result2Id, result3Id );

        List<QueryWorkInterface> work = new ArrayList<QueryWorkInterface>();
        work.add( queryWork );

        List<String> removes = new ArrayList<String>();
        removes.add( "//id" );
        
        List<String>emptyElementList = new ArrayList<String>();
        emptyElementList.add("//labTestPromises/labTests");
        emptyElementList.add("//labTestPromises");
        
        List<String>emptyElementLeafList = new ArrayList<String>();
        emptyElementLeafList.add("//labTestPromises/labTests/labCommentEvents");
        emptyElementLeafList.add("//labTestPromises/labSubscript");

        LabHibernateDomModelAssembler modelAssembler = new LabHibernateDomModelAssembler();
        modelAssembler.setRemoves( removes );
        modelAssembler.setEmptyElementList(emptyElementList);
        modelAssembler.setEmptyLeafElementList(emptyElementList);
        modelAssembler.setClinicalDataResponse( clinicalDataResponse );
        modelAssembler.assembleModel( entryFilter, work );
        
        EasyMock.verify( clinicalDataResponse, entryFilter, queryAssociation, queryWork, clinicalDataDocument, clinicalDataElement,
                        clinicalDataPatientElement, queryWorkResultsElement, result1, result2, result3, result1Id, result2Id, result3Id );
    }
    
    

    @SuppressWarnings( "unchecked" )
    @Test( expected = ModelAssemblerException.class )
    @Suite( groups = { "checkintest" } )
    public void testAssembleModelSingleWorkPartialAssemblyError( )
        throws Exception
    {
        List<Element> resultsList = new ArrayList<Element>();

        // initialize mocks for test
        ClinicalDataResponseInterface clinicalDataResponse = EasyMock.createMock( ClinicalDataResponseInterface.class );
        EntryFilterInterface entryFilter = EasyMock.createMock( EntryFilterInterface.class );
        QueryAssociationInterface queryAssociation = EasyMock.createMock( QueryAssociationInterface.class );
        QueryWorkInterface queryWork = EasyMock.createMock( QueryWorkInterface.class );
        Document clinicalDataDocument = EasyMock.createMock( Document.class );
        Element clinicalDataElement = EasyMock.createMock( Element.class );
        Element clinicalDataPatientElement = EasyMock.createMock( Element.class );
        Element queryWorkResultsElement = EasyMock.createMock( Element.class );

        Element result1 = EasyMock.createMock( Element.class );
        resultsList.add( result1 );
        
        EasyMock.expect( entryFilter.getTemplateId() ).andReturn( "templateId" );
        EasyMock.expect( entryFilter.getRequestId() ).andReturn( "requestId" );
        EasyMock.expect( clinicalDataResponse.buildEmptyClinicalDocumentWithPatient( "templateId", "requestId" ) ).andReturn( clinicalDataDocument );
        EasyMock.expect(clinicalDataResponse.getClinicalRoot( clinicalDataDocument )).andReturn( clinicalDataPatientElement );
        EasyMock.expect( clinicalDataPatientElement.getUniquePath()).andReturn( "//patient" ).anyTimes();
        
        
        // patient level processing
        EasyMock.expect( queryWork.getQueryAssociation() ).andReturn( queryAssociation );
        EasyMock.expect( queryAssociation.getParentKey() ).andReturn( null );
        EasyMock.expect( queryWork.getResults() ).andReturn( queryWorkResultsElement );
        EasyMock.expect( queryWorkResultsElement.hasContent() ).andReturn( Boolean.TRUE );
        EasyMock.expect( queryWorkResultsElement.elements() ).andReturn( resultsList );

        // reslult1 processing
//        EasyMock.expect( queryAssociation.getAssociationParent() ).andReturn( "patient" );
        EasyMock.expect( clinicalDataPatientElement.selectNodes( "//patient" ) ).andReturn( null );

        // throw exception processing
        EasyMock.expect( entryFilter.getDomainEntryPoint() ).andReturn( DomainEntryPoint.LabTestPromise.toString() );
        EasyMock.expect( queryWork.getQueryAssociation() ).andReturn( queryAssociation );
        EasyMock.expect( queryAssociation.getAssociationParent() ).andReturn( "patient" );
        
        
        // get ready to execute the test.
        EasyMock.replay( clinicalDataResponse, entryFilter, queryAssociation, queryWork, clinicalDataDocument, clinicalDataElement,
                        clinicalDataPatientElement, queryWorkResultsElement, result1 );

        List<QueryWorkInterface> work = new ArrayList<QueryWorkInterface>();
        work.add( queryWork );

        LabHibernateDomModelAssembler modelAssembler = new LabHibernateDomModelAssembler();
        modelAssembler.setClinicalDataResponse( clinicalDataResponse );
        modelAssembler.assembleModel( entryFilter, work );
        
        EasyMock.verify( clinicalDataResponse, entryFilter, queryAssociation, queryWork, clinicalDataDocument, clinicalDataElement,
                        clinicalDataPatientElement, queryWorkResultsElement, result1 );
    }
    
    @Test
    @Suite( groups = { "checkintest" } )
    public void testAssembleModelSingleWorkWithParent( )
        throws Exception
    {
        List<Element> resultsList = new ArrayList<Element>();
        List<Element> resultIdList = new ArrayList<Element>();

        // initialize mocks for test
        ClinicalDataResponseInterface clinicalDataResponse = EasyMock.createMock( ClinicalDataResponseInterface.class );
        EntryFilterInterface entryFilter = EasyMock.createMock( EntryFilterInterface.class );
        QueryAssociationInterface queryAssociation = EasyMock.createMock( QueryAssociationInterface.class );
        QueryWorkInterface queryWork = EasyMock.createMock( QueryWorkInterface.class );
        Document clinicalDataDocument = EasyMock.createMock( Document.class );
        Element clinicalDataElement = EasyMock.createMock( Element.class );
        Element clinicalDataPatientElement = EasyMock.createNiceMock( Element.class );
        Element queryWorkResultsElement = EasyMock.createMock( Element.class );

        Element result1 = EasyMock.createMock( Element.class );
        resultsList.add( result1 );


        Element result1Id = EasyMock.createMock( Element.class );
       
        resultIdList.add( result1Id );

        // set the expectations on the mocks.
        EasyMock.expect( entryFilter.getTemplateId() ).andReturn( "templateId" );
        EasyMock.expect( entryFilter.getRequestId() ).andReturn( "requestId" );
        EasyMock.expect( clinicalDataResponse.buildEmptyClinicalDocumentWithPatient( "templateId", "requestId" ) ).andReturn( clinicalDataDocument );
        EasyMock.expect( clinicalDataResponse.getClinicalRoot( clinicalDataDocument )).andReturn( clinicalDataPatientElement ).anyTimes();
        EasyMock.expect( clinicalDataPatientElement.getUniquePath()).andReturn( "//patient" ).anyTimes();
        
        // patient level processing
        EasyMock.expect( queryWork.getQueryAssociation() ).andReturn( queryAssociation );
        EasyMock.expect( queryAssociation.getParentKey() ).andReturn( "labTestPromise" ).anyTimes();
        EasyMock.expect( queryWork.getResults() ).andReturn( queryWorkResultsElement );
        EasyMock.expect( queryWorkResultsElement.hasContent() ).andReturn( Boolean.TRUE );
        EasyMock.expect(queryWorkResultsElement.elements() ).andReturn( resultsList );

        List clinicalDataPatientElementList = new ArrayList();
        clinicalDataPatientElementList.add( clinicalDataPatientElement );
        
        //setup some parent key processing 
        EasyMock.expect( result1.elementText("labTestPromise")).andReturn("labTestPromise") ;
        EasyMock.expect( queryAssociation.getAssociationParent()).andReturn( "patient" );
        EasyMock.expect( queryAssociation.getKey()).andReturn( "id" );
        
       
        // reslult1 processing
//        EasyMock.expect( queryAssociation.getAssociationParent() ).andReturn( "patient" );
        EasyMock.expect( clinicalDataPatientElement.selectNodes( "//patient/patient[id='labTestPromise']" ) ).andReturn( clinicalDataPatientElementList );
       // EasyMock.expect( clinicalDataPatientElement.selectNodes( "//patient" ) ).andReturn( clinicalDataPatientElementList );
        EasyMock.expect( queryAssociation.isCollapsable() ).andReturn( false );
        EasyMock.expect( result1.detach() ).andReturn( result1 );
        clinicalDataPatientElement.add( ( Element )result1 );

     

        // id removal processing
        EasyMock.expect( clinicalDataPatientElement.selectNodes( "//id" ) ).andReturn( resultIdList );
        EasyMock.expect( result1Id.detach() ).andReturn( ( Node )result1Id );
        
        // get ready to execute the test.
        EasyMock.replay( clinicalDataResponse, entryFilter, queryAssociation, queryWork, clinicalDataDocument, clinicalDataElement,
                        clinicalDataPatientElement, queryWorkResultsElement, result1,  result1Id);

        List<QueryWorkInterface> work = new ArrayList<QueryWorkInterface>();
        work.add( queryWork );

        List<String> removes = new ArrayList<String>();
        removes.add( "//id" );
        
        List<String>emptyElementList = new ArrayList<String>();
        emptyElementList.add("//labTestPromises/labTests");
        emptyElementList.add("//labTestPromises");
        
        List<String>emptyElementLeafList = new ArrayList<String>();
        emptyElementLeafList.add("//labTestPromises/labTests/labCommentEvents");
        emptyElementLeafList.add("//labTestPromises/labSubscript");

        LabHibernateDomModelAssembler modelAssembler = new LabHibernateDomModelAssembler();
        modelAssembler.setRemoves( removes );
        modelAssembler.setEmptyElementList(emptyElementList);
        modelAssembler.setEmptyLeafElementList(emptyElementList);
        modelAssembler.setClinicalDataResponse( clinicalDataResponse );
        modelAssembler.assembleModel( entryFilter, work );
        
        EasyMock.verify( clinicalDataResponse, entryFilter, queryAssociation, queryWork, clinicalDataDocument, clinicalDataElement,
                        clinicalDataPatientElement, queryWorkResultsElement, result1,  result1Id );
    }


    private void expectQueryWorkCheck( Element parent, String parentName, QueryWorkInterface queryWork, QueryAssociationInterface queryAssociation,
                    String associationName )
    {
        EasyMock.expect( parent.getName() ).andReturn( parentName );
        EasyMock.expect( queryWork.getQueryAssociation() ).andReturn( queryAssociation );
        EasyMock.expect( queryAssociation.getAssociationParent() ).andReturn( associationName );
    }
}
