/*! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.repository;

import java.util.Collections;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.pentaho.di.core.ObjectLocationSpecificationMethod;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleMissingPluginsException;
import org.pentaho.di.core.exception.KettleXMLException;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.job.entry.JobEntryCopy;
import org.pentaho.di.job.entry.JobEntryInterface;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import static org.mockito.Mockito.*;

@RunWith( MockitoJUnitRunner.class )
public class RepositoryImporterTest {

  private static final String ROOT_PATH = "/test_root";

  private static final String USER_NAME_PATH = "/userName";

  @Mock
  private RepositoryImportFeedbackInterface feedback;

  private RepositoryDirectoryInterface baseDirectory;

  private Node entityNode;

  @Before
  public void beforeTest() {
    NodeList nodeList = mock( NodeList.class );

    entityNode = mock( Node.class );
    when( entityNode.getChildNodes() ).thenReturn( nodeList );

    baseDirectory = mock( RepositoryDirectoryInterface.class );
    when( baseDirectory.getPath() ).thenReturn( ROOT_PATH );
  }

  @Test
  public void testImportJob_patchJobEntries_without_variables() throws KettleException {
    JobEntryInterface jobEntry = createJobEntry( "/userName" );
    StepMetaInterface stepMeta = createStepMeta( "" );
    RepositoryImporter importer = createRepositoryImporter( jobEntry, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importJob( entityNode, feedback );
    verify( (HasRepositoryDirectories) jobEntry  ).setDirectories( new String[]{ ROOT_PATH + USER_NAME_PATH } );
  }

  @Test
  public void testImportJob_patchJobEntries_with_variable() throws KettleException {
    JobEntryInterface jobEntryInterface = createJobEntry( "${USER_VARIABLE}" );
    StepMetaInterface stepMeta = createStepMeta( "" );
    RepositoryImporter importer = createRepositoryImporter( jobEntryInterface, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importJob( entityNode, feedback );
    verify( (HasRepositoryDirectories) jobEntryInterface ).setDirectories( new String[]{ "${USER_VARIABLE}" } );
  }

  @Test
  public void testImportJob_patchJobEntries_when_directory_path_starts_with_variable() throws KettleException {
    JobEntryInterface jobEntryInterface = createJobEntry( "${USER_VARIABLE}/myDir" );
    StepMetaInterface stepMeta = createStepMeta( "" );
    RepositoryImporter importer = createRepositoryImporter( jobEntryInterface, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importJob( entityNode, feedback );
    verify( (HasRepositoryDirectories) jobEntryInterface ).setDirectories( new String[] { "${USER_VARIABLE}/myDir" } );

    JobEntryInterface jobEntryInterface2 = createJobEntry( "${USER_VARIABLE}/myDir" );
    RepositoryImporter importerWithCompatibilityImportPath =
        createRepositoryImporter( jobEntryInterface2, stepMeta, false );
    importerWithCompatibilityImportPath.setBaseDirectory( baseDirectory );

    importerWithCompatibilityImportPath.importJob( entityNode, feedback );
    verify( (HasRepositoryDirectories) jobEntryInterface2 ).setDirectories( new String[]{ ROOT_PATH + "/${USER_VARIABLE}/myDir" } );
  }

  @Test
  public void testImportJob_patchJobEntries_when_directory_path_ends_with_variable() throws KettleException {
    JobEntryInterface jobEntryInterface = createJobEntry( "/myDir/${USER_VARIABLE}" );
    StepMetaInterface stepMeta = createStepMeta( "" );
    RepositoryImporter importer = createRepositoryImporter( jobEntryInterface, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importJob( entityNode, feedback );
    verify( (HasRepositoryDirectories) jobEntryInterface ).setDirectories( new String[] { "/myDir/${USER_VARIABLE}" } );

    JobEntryInterface jobEntryInterface2 = createJobEntry( "/myDir/${USER_VARIABLE}" );
    RepositoryImporter importerWithCompatibilityImportPath =
        createRepositoryImporter( jobEntryInterface2, stepMeta, false );
    importerWithCompatibilityImportPath.setBaseDirectory( baseDirectory );

    importerWithCompatibilityImportPath.importJob( entityNode, feedback );
    verify( (HasRepositoryDirectories) jobEntryInterface2 ).setDirectories( new String[] { ROOT_PATH + "/myDir/${USER_VARIABLE}" } );
  }

  @Test
  public void testImportTrans_patchTransEntries_without_variables() throws KettleException {
    JobEntryInterface jobEntryInterface = createJobEntry( "" );
    StepMetaInterface stepMeta = createStepMeta( "/userName" );
    RepositoryImporter importer = createRepositoryImporter( jobEntryInterface, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importTransformation( entityNode, feedback );
    verify( (HasRepositoryDirectories) stepMeta ).setDirectories( new String[] { ROOT_PATH + USER_NAME_PATH } );
  }

  @Test
  public void testImportTrans_patchTransEntries_with_variable() throws KettleException {
    JobEntryInterface jobEntryInterface = createJobEntry( "" );
    StepMetaInterface stepMeta = createStepMeta(  "${USER_VARIABLE}" );
    RepositoryImporter importer = createRepositoryImporter( jobEntryInterface, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importTransformation( entityNode, feedback );
    verify( (HasRepositoryDirectories) stepMeta ).setDirectories( new String[] { "${USER_VARIABLE}" } );
  }

  @Test
  public void testImportTrans_patchTransEntries_when_directory_path_starts_with_variable() throws KettleException {
    JobEntryInterface jobEntryInterface = createJobEntry( "" );
    StepMetaInterface stepMeta = createStepMeta( "${USER_VARIABLE}/myDir" );
    RepositoryImporter importer = createRepositoryImporter( jobEntryInterface, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importTransformation( entityNode, feedback );
    verify( (HasRepositoryDirectories) stepMeta ).setDirectories( new String[] { "${USER_VARIABLE}/myDir" } );

    StepMetaInterface stepMeta2 = createStepMeta( "${USER_VARIABLE}/myDir" );
    RepositoryImporter importerWithCompatibilityImportPath =
        createRepositoryImporter( jobEntryInterface, stepMeta2, false );
    importerWithCompatibilityImportPath.setBaseDirectory( baseDirectory );

    importerWithCompatibilityImportPath.importTransformation( entityNode, feedback );
    verify( (HasRepositoryDirectories) stepMeta2 ).setDirectories( new String[] { ROOT_PATH + "/${USER_VARIABLE}/myDir" } );
  }

  @Test
  public void testImportTrans_patchTransEntries_when_directory_path_ends_with_variable() throws KettleException {
    JobEntryInterface jobEntryInterface = createJobEntry( "" );
    StepMetaInterface stepMeta = createStepMeta(  "/myDir/${USER_VARIABLE}" );
    RepositoryImporter importer = createRepositoryImporter( jobEntryInterface, stepMeta, true );
    importer.setBaseDirectory( baseDirectory );

    importer.importTransformation( entityNode, feedback );
    verify( (HasRepositoryDirectories) stepMeta ).setDirectories( new String[] { "/myDir/${USER_VARIABLE}" } );

    StepMetaInterface stepMeta2 = createStepMeta( "/myDir/${USER_VARIABLE}" );
    RepositoryImporter importerWithCompatibilityImportPath =
        createRepositoryImporter( jobEntryInterface, stepMeta2, false );
    importerWithCompatibilityImportPath.setBaseDirectory( baseDirectory );

    importerWithCompatibilityImportPath.importTransformation( entityNode, feedback );
    verify( (HasRepositoryDirectories) stepMeta2 ).setDirectories( new String[] { ROOT_PATH + "/myDir/${USER_VARIABLE}" } );
  }

  private static JobEntryInterface createJobEntry( String directory ) {
    JobEntryInterface jobEntryInterface = mock( JobEntryInterface.class, withSettings().extraInterfaces( HasRepositoryDirectories.class ) );
    when( jobEntryInterface.isReferencedObjectEnabled() ).thenReturn( new boolean[] { true } );
    doAnswer( invocationOnMock -> new ObjectLocationSpecificationMethod[] { ObjectLocationSpecificationMethod.REPOSITORY_BY_NAME } )
      .when( ( (HasRepositoryDirectories) jobEntryInterface ) ).getSpecificationMethods();
    doAnswer( invocationOnMock -> new String[] { directory } )
      .when( (HasRepositoryDirectories) jobEntryInterface ).getDirectories();
    return jobEntryInterface;
  }

  private static StepMetaInterface createStepMeta( String directory ) {
    StepMetaInterface stepMetaInterface = mock( StepMetaInterface.class, withSettings().extraInterfaces( HasRepositoryDirectories.class ) );
    when( stepMetaInterface.isReferencedObjectEnabled() ).thenReturn( new boolean[] { true } );
    doAnswer( invocationOnMock -> new ObjectLocationSpecificationMethod[] { ObjectLocationSpecificationMethod.REPOSITORY_BY_NAME } )
      .when( ( (HasRepositoryDirectories) stepMetaInterface ) ).getSpecificationMethods();
    doAnswer( invocationOnMock -> new String[] { directory } )
      .when( (HasRepositoryDirectories) stepMetaInterface ).getDirectories();
    return stepMetaInterface;
  }

  private static RepositoryImporter createRepositoryImporter( final JobEntryInterface jobEntryInterface, final
                                                              StepMetaInterface stepMetaInterface,
                                                              final boolean needToCheckPathForVariables ) {
    Repository repository = mock( Repository.class );
    LogChannelInterface log = mock( LogChannelInterface.class );
    RepositoryImporter importer = new RepositoryImporter( repository, log ) {

      @Override
      JobMeta createJobMetaForNode( Node jobnode ) throws KettleXMLException {
        JobMeta meta = mock( JobMeta.class );
        JobEntryCopy jec = mock( JobEntryCopy.class );
        when( jec.isTransformation() ).thenReturn( true );
        when( jec.getEntry() ).thenReturn( jobEntryInterface );
        when( meta.getJobCopies() ).thenReturn( Collections.singletonList( jec ) );
        return meta;
      }

      @Override
      TransMeta createTransMetaForNode( Node transnode ) throws KettleMissingPluginsException, KettleXMLException {
        TransMeta meta = mock( TransMeta.class );
        StepMeta stepMeta = mock( StepMeta.class );
        when( stepMeta.isMapping() ).thenReturn( true );
        when( stepMeta.getStepMetaInterface() ).thenReturn( stepMetaInterface );
        when( meta.getSteps() ).thenReturn( Collections.singletonList( stepMeta ) );
        return meta;
      }

      @Override
      protected void replaceSharedObjects( JobMeta transMeta ) throws KettleException {
      }

      @Override
      protected void replaceSharedObjects( TransMeta transMeta ) throws KettleException {
      }

      @Override
      boolean needToCheckPathForVariables() {
        return needToCheckPathForVariables;
      }
    };
    return importer;
  }

}