

package gov.va.med.cds.filter;


import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import gov.va.med.cds.AbstractBaseIntegrationCoreTest;
import gov.va.med.cds.clinicaldata.DomainEntryPoint;
import gov.va.med.cds.clinicaldata.EntryFilter;
import gov.va.med.cds.clinicaldata.FilterMetaData;
import gov.va.med.cds.clinicaldata.FilterMetaDataInterface;
import gov.va.med.cds.exception.FilterCacheException;
import gov.va.med.cds.junit.runners.AfterTestsOnce;
import gov.va.med.cds.junit.runners.BeforeTestsOnce;
import gov.va.med.cds.junit.runners.Suite;
import gov.va.med.cds.junit.runners.SuiteAwareSpringRunner;
import gov.va.med.cds.tfs.util.FilterMetaDataHelper;
import gov.va.med.cds.util.StreamUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;

import java.util.HashSet;
import java.util.Set;


/**
 * Here are the methods that will be tested from the FilterCachePersistence and OracleFilterCachePersistence classes:
 * 
 * <ul>
 * <li>public void persistFilterSchema( String filterId, String filterSchema, Properties filterProps );
 * <li>public void loadAllFilterSchemasIntoCache( FilterCacheInterface filterCacheInterface );
 * <li>public void loadFilterSchemaIntoCache(String filterId, FilterCacheInterface filterCache );
 * <li>public void removeFilterSchema( String filterId );
 * </ul>
 */
@RunWith( SuiteAwareSpringRunner.class )
@ContextConfiguration( locations = { "classpath:gov/va/med/cds/config/nonWeblogicApplicationContext.xml" } )
//@DirtiesContext(classMode=ClassMode.AFTER_CLASS)
public class OracleFilterCachePersistenceTest
    extends
        AbstractBaseIntegrationCoreTest
{

    /*
     * Here are the various variables needed.
     * 
     * The samples schemas should be read from a file or resource. Otherwise we must manually escape the double quotes
     * used throughout the schema.
     * 
     */
    private static final Log LOGGER = LogFactory.getLog( OracleFilterCachePersistenceTest.class );
    private static final String VHIM_VERSION = "Vhim_4_00";

    // Path to the Schemas
    private static final String SCHEMA_PATH = "classpath:gov/va/med/cds/filter/";

    // The actual schema filenames
    private static final String FILTER_100_SCHEMA = "Filter100.xsd";
    private static final String RDI_IC_RX_FILTER_1 = "RDI_IC_Rx_Filter_1.xsd";
    private static final String RDI_IC_RX_FILTER_WTIH_FIXED_XPATH = "RDI_IC_Rx_Filter_With_Fixed_XPath.xsd";

    // The Filter Schema IDs that will be used
    private static final String FILTER_ID_1 = "FilterSchema_Test_001";
    private static final String FILTER_ID_2 = "FilterSchema_Test_002";
    private static final String FILTER_ID_3 = "FilterSchema_Test_003";
    private static final String FILTER_ID_4 = "FilterSchema_Test_004";

    // The actual Sample Schema is read in from the files
    private static String theSampleSchema01 = "";
    private static String theSampleSchema02 = "";
    private static String theSampleSchema03 = "";

    // The FilterCache and the Persistence
    private FilterMemoryCache filterMemoryCache = null;
    private static FilterCachePersistenceInterface filterCachePersistence = null;

    private Set<EntryFilter> theEntryFilters = null;
    private ResourceLoader resourceLoader = new DefaultResourceLoader();


    /**
     * Perform test initialization prior to methods getting called.
     */
    @BeforeTestsOnce
    @Suite( groups = { "checkintest" } )
    public void beforeOracleFilterCachePersistenceTestClassSetUp( )
        throws Exception
    {
        removeFilterSchemas();
        LOGGER.debug( "\nOracleFilterCachePersistenceTest starting..." );

        // Read in a sample schema against which to match the filters.
        Resource resource = resourceLoader.getResource( SCHEMA_PATH + FILTER_100_SCHEMA );
        theSampleSchema01 = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( SCHEMA_PATH + RDI_IC_RX_FILTER_1 );
        theSampleSchema02 = StreamUtil.resourceToString( resource );

        resource = resourceLoader.getResource( SCHEMA_PATH + RDI_IC_RX_FILTER_WTIH_FIXED_XPATH );
        theSampleSchema03 = StreamUtil.resourceToString( resource );

        LOGGER.debug( "\nThe Sample Filter Schema is:" );
        LOGGER.debug( "Sample Schema 01: " + theSampleSchema01 );
        LOGGER.debug( "Sample Schema 02: " + theSampleSchema02 );
        LOGGER.debug( "Sample Schema 03: " + theSampleSchema03 );

    }


    @Test
    @Suite( groups = { "checkintest" } )
    public void testPersistenceObject( )
    {
        assertNotNull( filterCachePersistence );
        assertNotNull( filterMemoryCache );
    }


    /**
     * Added for convenience since moving from single to multiple allowable vhim versions.
     * @param aFilter
     * @param aVhimVersion
     */
    private void setVhimVersion( FilterMetaDataInterface aFilter, String aVhimVersion )
    {
        HashSet<String> vhimVersions = new HashSet<String>();
        vhimVersions.add( aVhimVersion );
        aFilter.setVhimVersions( vhimVersions );
    }


    /**
     * Test persistence by adding three schemas to the Filter Schema table in the database.
     * 
     * This test assumes that there is a VHIM_VERSION (Vhim_4_00 entry) in the VHIM_Version table.
     * 
     */
    @Test
    @Suite( order = 2, groups = { "checkintest" } )
    public void testPersistFilterSchema( )
    {
        LOGGER.debug( "testPersistFilterSchema..." );

        removeFilterSchemas();

        // Stuff values into the Metadata then persist the samples to the database.
        FilterMetaDataInterface theMetadata = new FilterMetaData();

        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_1 );
        theMetadata.setFilterSchemaXml( theSampleSchema01 );
        theEntryFilters = new HashSet<EntryFilter>();
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( DomainEntryPoint.AllergyAssessment ) );
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( DomainEntryPoint.OutpatientMedicationPromise ) );
        theMetadata.setEntryFilters( theEntryFilters );
        filterCachePersistence.persistFilterSchema( theMetadata );

        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_2 );
        theMetadata.setFilterSchemaXml( theSampleSchema02 );
        theEntryFilters = new HashSet<EntryFilter>();
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( DomainEntryPoint.IntoleranceCondition ) );
        theMetadata.setEntryFilters( theEntryFilters );
        filterCachePersistence.persistFilterSchema( theMetadata );

        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_3 );
        theMetadata.setFilterSchemaXml( theSampleSchema03 );
        theEntryFilters = new HashSet<EntryFilter>();
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( DomainEntryPoint.AllergyAssessment ) );
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( DomainEntryPoint.OutpatientMedicationPromise ) );
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( DomainEntryPoint.VitalSignObservationEvent ) );
        theMetadata.setEntryFilters( theEntryFilters );
        filterCachePersistence.persistFilterSchema( theMetadata );
    }


    /**
     * Test loading all filter schemas
     */
    @Test
    @Suite( order = 3, groups = { "checkintest" }, dependsOnMethods = { "testPersistFilterSchema" } )
    public void testLoadAllFilterSchemasIntoCache( )
    {
        LOGGER.debug( "testLoadAllFilterSchemasIntoCache..." );
        filterCachePersistence.loadAllFilterSchemasIntoMemoryCache( filterMemoryCache );

        assertTrue( filterMemoryCache.getNumberOfFilterSchemasLoadedIntoFilterCache() > 0 );
    }


    /**
     * Test loading several filter schemas
     */
    @Test
    @Suite( order = 4, groups = { "checkintest" }, dependsOnMethods = { "testLoadAllFilterSchemasIntoCache" } )
    public void testLoadFilterSchemaIntoCache( )
    {
        LOGGER.debug( "testLoadFilterSchemaIntoCache..." );

        filterCachePersistence.loadFilterIntoMemoryCache( FILTER_ID_1, filterMemoryCache );
        assertTrue( filterMemoryCache.getNumberOfFilterSchemasLoadedIntoFilterCache() >= 1 );
        assertNotNull( filterMemoryCache.getFilterString( FILTER_ID_1 ) );

        filterCachePersistence.loadFilterIntoMemoryCache( FILTER_ID_2, filterMemoryCache );
        assertTrue( filterMemoryCache.getNumberOfFilterSchemasLoadedIntoFilterCache() >= 1 );
        assertNotNull( filterMemoryCache.getFilterString( FILTER_ID_2 ) );

        filterCachePersistence.loadFilterIntoMemoryCache( FILTER_ID_3, filterMemoryCache );
        assertTrue( filterMemoryCache.getNumberOfFilterSchemasLoadedIntoFilterCache() >= 1 );
        assertNotNull( filterMemoryCache.getFilterString( FILTER_ID_3 ) );
    }


    /**
     * Test removal of a schema by adding a single schema and then deleting it.
     * 
     * This test assumes that there is a Vhim_4_00 entry in the VHIM_Version table.
     * 
     */
    @Test
    @Suite( order = 5, groups = { "checkintest" }, dependsOnMethods = { "testLoadFilterSchemaIntoCache" } )
    public void testRemoveFilterSchema( )
    {
        LOGGER.debug( "testPersistFilterSchema..." );

        filterCachePersistence.removeFilterSchema( FILTER_ID_1 );
        // See if the Schema is still in the database. It should fail.
        try
        {
            filterCachePersistence.loadFilterIntoMemoryCache( FILTER_ID_1, filterMemoryCache );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
        }

        filterCachePersistence.removeFilterSchema( FILTER_ID_2 );
        // See if the Schema is still in the database. It should fail.
        try
        {
            filterCachePersistence.loadFilterIntoMemoryCache( FILTER_ID_2, filterMemoryCache );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
        }

        filterCachePersistence.removeFilterSchema( FILTER_ID_3 );
        // See if the Schema is still in the database. It should fail.
        try
        {
            filterCachePersistence.loadFilterIntoMemoryCache( FILTER_ID_3, filterMemoryCache );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
        }
    }


    /**
     * Test persist with Bad Metadata for the Filter Schema.
     * 
     * This test assumes that there is a VHIM_VERSION (Vhim_4_00) entry in the VHIM_Version table.
     * 
     */
    @Test
    @Suite( groups = { "checkintest" } )
    public void testBadMetadataFilterSchema( )
    {
        LOGGER.debug( "testPersistFilterSchema..." );

        // Stuff values into the Metadata then persist the samples to the database.
        FilterMetaDataInterface theMetadata = new FilterMetaData();
        // First try no metadata at all
        try
        {
            filterCachePersistence.persistFilterSchema( null );
            fail( "It worked with no metadata!" );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
            LOGGER.debug( "No metadata failed correctly! " + e.getMessage() );
        }
        catch ( Exception e )
        {
            assertTrue( true );
            LOGGER.debug( "No metadata failed correctly! " + e.getMessage() );
        }

        // Now try a bad VHIM Version
        /*
         * This test is invalid - there's nothing to stop a bad vhim version from being stored

                setVhimVersion( theMetadata, "Bozo_the_Clown" );
                try
                {
                    filterCachePersistence.persistFilterSchema( theMetadata );
                    fail( "Metadata with a wrong VHIM Version should not succeed!" );
                }
                catch ( FilterCacheException e )
                {
                    assertTrue( true );
                    LOGGER.debug( "Bad VHIM Version was caught! " + e.getMessage() );
                }
         */

        // Now try a good VHIM Version
        setVhimVersion( theMetadata, VHIM_VERSION );
        try
        {
            filterCachePersistence.persistFilterSchema( theMetadata );
            fail( "Metadata with a good VHIM Version should not succeed. Still missing parts!" );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
            LOGGER.debug( "Good VHIM Version with missing other data was caught! " + e.getMessage() );
        }

        // Now try VHIM Version + Filter Id
        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_4 );
        theMetadata.setFilterSchemaXml( null );
        try
        {
            filterCachePersistence.persistFilterSchema( theMetadata );
            fail( "A null FilterSchema was sent and it worked!" );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
            LOGGER.debug( "A null FilterSchema was caught! " + e.getMessage() );
        }

        // Now try VHIM Version + Filter Id
        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( "Roger_the_Rabbit" );
        theMetadata.setFilterSchemaXml( null );
        try
        {
            filterCachePersistence.persistFilterSchema( theMetadata );
            fail( "A bad FilterId and a null FilterSchema was sent and it worked!" );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
            LOGGER.debug( "A bad FilterId and a null FilterSchema was caught! " + e.getMessage() );
        }

        // Now try VHIM Version + Filter Id + Filter Schema
        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_4 );
        theMetadata.setFilterSchemaXml( theSampleSchema01 );
        try
        {
            filterCachePersistence.persistFilterSchema( theMetadata );
            fail( "No Domain Entry Points in the Metadata succeeded incorrectly!" );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
            LOGGER.debug( "No Domain Entry Points in the Metadata failed correctly! " + e.getMessage() );
        }
        catch ( Exception e )
        {
            assertTrue( true );
            LOGGER.debug( "Caught No Domain Entry Point error so it was a success! " + e.getMessage() );
        }

        // Now try VHIM Version + Filter Id + Filter Schema + bad Domain Entry Point not in database
        // How to force a bad Domain Entry Point lookup in the database? The enumeration prevents this.
        // Create a null Domain Entry Point
        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_4 );
        theMetadata.setFilterSchemaXml( theSampleSchema01 );
        Set<EntryFilter> theEntryFilters = new HashSet<EntryFilter>();
        theEntryFilters.add( new EntryFilter() );
        theMetadata.setEntryFilters( theEntryFilters );
        try
        {
            filterCachePersistence.persistFilterSchema( theMetadata );
            LOGGER.debug( "Persisting schema worked! " );
            fail( "should not have saved with empty entryfilters! " );
        }
        catch ( FilterCacheException e )
        {
            assertTrue( true );
        }
        catch ( Exception e )
        {
            fail( "should have failed with filtercache exception! " );
        }

        // Now try VHIM Version + Filter Id + Filter Schema + Domain Entry Points - All metadata present
        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_4 );
        theMetadata.setFilterSchemaXml( theSampleSchema01 );
        theEntryFilters = new HashSet<EntryFilter>();
        DomainEntryPoint allergy = DomainEntryPoint.AllergyAssessment;
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( allergy ) );
        DomainEntryPoint pharmacy = DomainEntryPoint.OutpatientMedicationPromise;
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( pharmacy ) );
        theMetadata.setEntryFilters( theEntryFilters );
        try
        {
            filterCachePersistence.persistFilterSchema( theMetadata );
            LOGGER.debug( "Persisting schema worked! " );
            assertTrue( true );
        }
        catch ( FilterCacheException e )
        {
            fail( "An error occured!" + e.getMessage() );
        }
    }


    /**
     * Try to add/update a filter schema that already exists in the database. It will not fail to add an existing schema
     * it will merely update the one that exists.
     * 
     * testUpdateExistingFilterSchema
     */
    @Test
    @Suite( order = 1, groups = { "checkintest" } )
    public void testUpdateExistingFilterSchema( )
    {
        LOGGER.debug( "testAddExistingFilterSchema..." );

        removeFilterSchemas();

        // Stuff values into the Metadata then persist the sample to the database.
        FilterMetaDataInterface theMetadata = new FilterMetaData();
        setVhimVersion( theMetadata, VHIM_VERSION );
        theMetadata.setFilterId( FILTER_ID_1 );
        theMetadata.setFilterSchemaXml( theSampleSchema01 );
        Set<EntryFilter> theEntryFilters = new HashSet<EntryFilter>();
        DomainEntryPoint allergy = DomainEntryPoint.AllergyAssessment;
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( allergy ) );
        DomainEntryPoint pharmacy = DomainEntryPoint.OutpatientMedicationPromise;
        theEntryFilters.add( FilterMetaDataHelper.getEntryFilter( pharmacy ) );
        theMetadata.setEntryFilters( theEntryFilters );
        try
        {
            filterCachePersistence.persistFilterSchema( theMetadata );
            LOGGER.debug( "Successfully updated 001" );
        }
        catch ( FilterCacheException e )
        {
            fail( "An error occured updating an existing schema! " + e.getMessage() );
        }
    }


    /**
     * Perform test finalization after the other methods are finished.
     * 
     * @throws Exception
     */
    @AfterTestsOnce
    @Suite( groups = { "checkintest" } )
    public void oracleFilterCachePersistenceTestTearDown( )
        throws Exception
    {
        /*
         * Remove the dummy records and the records generated as part of the test.
         * 
         */
        removeFilterSchemas();
        LOGGER.debug( "OracleFilterCachePersistenceTest finished!\n" );
    }


    /**
     * Here are the Spring Dependency Injection methods to set the FilterCache and the FilterCachePersistence
     */
    @javax.annotation.Resource
    public void setFilterCachePersistence( FilterCachePersistenceInterface filterCachePersistence )
    {
        OracleFilterCachePersistenceTest.filterCachePersistence = filterCachePersistence;
    }


    /**
     * Setter method for the FilterCache
     * 
     * @param aFilterCache
     */
    @javax.annotation.Resource
    public void setFilterCache( FilterMemoryCache aFilterCache )
    {
        this.filterMemoryCache = aFilterCache;
    }


    private static void removeFilterSchemas( )
    {
        filterCachePersistence.removeFilterSchema( FILTER_ID_1 );
        filterCachePersistence.removeFilterSchema( FILTER_ID_2 );
        filterCachePersistence.removeFilterSchema( FILTER_ID_3 );
        filterCachePersistence.removeFilterSchema( FILTER_ID_4 );
    }

}
