/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 * Modified by
 * @author  Data Virtuality GmbH
 * @date    $Date$
 *  
 */
package org.teiid.translator.ldap;

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

import javax.naming.directory.SearchControls;
import javax.naming.ldap.SortKey;
import org.junit.jupiter.api.Test;
import org.teiid.cdk.CommandBuilder;
import org.teiid.language.Command;
import org.teiid.language.Select;
import org.teiid.metadata.Column;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.unittest.RealMetadataFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;

/** 
 * Test IQueryToLdapSearchParser.  
 */
/**
 * @author mdrilling
 *
 */
public class TestIQueryToLdapSearchParser {
    
    /**
     * Get Resolved Command using SQL String and metadata.
     */
    public Command getCommand(String sql, QueryMetadataInterface metadata) {
    	CommandBuilder builder = new CommandBuilder(metadata);
    	return builder.getCommand(sql);
    }
    
	/**
     * Helper method for testing the provided LDAPSearchDetails against expected values
     * @param searchDetails the LDAPSearchDetails object
     * @param expectedContextName the expected context name
     * @param expectedContextFilter the expected context filter string
     * @param expectedAttrNameList list of expected attribute names
     * @param expectedCountLimit the expected count limit
     * @param expectedSearchScope the expected search scope
     * @param expectedSortKeys the expected sortKeys list.
     */
    public void helpTestSearchDetails(final LDAPSearchDetails searchDetails, final String expectedContextName,
    		final String expectedContextFilter, final List expectedAttrNameList, final long expectedCountLimit, 
    		final int expectedSearchScope, final SortKey[] expectedSortKeys) {
    	
    	// Get all of the actual values
        String contextName = searchDetails.getContextName();
        String contextFilter = searchDetails.getContextFilter();
        // EXTENSION
        // ISSUE: DVCORE-2440
        // FIXED BY: Roman Pedchenko
        // DATE: 2014.10.18
        // ADOPTED FROM: https://issues.jboss.org/browse/TEIID-2176,https://issues.jboss.org/browse/TEIID-2177
        // List attrList = searchDetails.getAttributeList();
        List attrList = searchDetails.getElementList();
        // END EXTENSION
        long countLimit = searchDetails.getCountLimit();
    	int searchScope = searchDetails.getSearchScope();
    	SortKey[] sortKeys = searchDetails.getSortKeys();
    	
    	// Compare actual with Expected
    	assertEquals(expectedContextName, contextName);
    	assertEquals(expectedContextFilter, contextFilter);
    	
    	assertEquals(attrList.size(),expectedAttrNameList.size());
    	Iterator iter = attrList.iterator();
    	Iterator eIter = expectedAttrNameList.iterator();
    	while(iter.hasNext()&&eIter.hasNext()) {
			// EXTENSION
			// ISSUE: DVCORE-2440
			// FIXED BY: Roman Pedchenko
			// DATE: 2014.10.18
			// ADOPTED FROM: https://issues.jboss.org/browse/TEIID-2176,https://issues.jboss.org/browse/TEIID-2177
			// String actualName = ((Attribute)iter.next()).getID();
			String actualName = IQueryToLdapSearchParser.getNameFromElement((Column)iter.next());
			// END EXTENSION
			String expectedName = (String)eIter.next();
			assertEquals(actualName, expectedName);
    	}

    	assertEquals(expectedCountLimit, countLimit);
    	assertEquals(expectedSearchScope, searchScope);
    	assertEquals(expectedSortKeys, sortKeys);
    }

	/**
     * Test a Query without criteria
     */
	@Test
    public void testSelectFrom1() throws Exception {
        LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT UserID, Name FROM LdapModel.People"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(objectClass=*)"; //$NON-NLS-1$
        
        List expectedAttrNameList = new ArrayList();
        expectedAttrNameList.add("uid"); //$NON-NLS-1$
        expectedAttrNameList.add("cn"); //$NON-NLS-1$
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
        		expectedCountLimit, expectedSearchScope, expectedSortKeys);
        
    }
    
	/**
     * Test a Query with a criteria
     */
	@Test
    public void testSelectFromWhere1() throws Exception {
    	LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT UserID, Name FROM LdapModel.People WHERE Name = 'R%'"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(cn=R%)"; //$NON-NLS-1$
        
        List expectedAttrNameList = new ArrayList();
        expectedAttrNameList.add("uid"); //$NON-NLS-1$
        expectedAttrNameList.add("cn"); //$NON-NLS-1$
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
        		expectedCountLimit, expectedSearchScope, expectedSortKeys);
        
    }
    
	/**
     * Test a Query with a criteria
     */
	@Test
    public void testEscaping() throws Exception {
    	LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT UserID, Name FROM LdapModel.People WHERE Name = 'R*'"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(cn=R\\2a)"; //$NON-NLS-1$
        
        List expectedAttrNameList = new ArrayList();
        expectedAttrNameList.add("uid"); //$NON-NLS-1$
        expectedAttrNameList.add("cn"); //$NON-NLS-1$
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
        		expectedCountLimit, expectedSearchScope, expectedSortKeys);
        
    }

	@Test
    public void testNot() throws Exception {
    	LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT UserID, Name FROM LdapModel.People WHERE not (Name like 'R%' or Name like 'S%')"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(&(!(cn=R*))(!(cn=S*)))"; //$NON-NLS-1$
        
        List expectedAttrNameList = new ArrayList();
        expectedAttrNameList.add("uid"); //$NON-NLS-1$
        expectedAttrNameList.add("cn"); //$NON-NLS-1$
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
        		expectedCountLimit, expectedSearchScope, expectedSortKeys);
        
    }

	@Test
    public void testGT() throws Exception {
    	LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT UserID, Name FROM LdapModel.People WHERE Name > 'R'"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(!(cn<=R))"; //$NON-NLS-1$
        
        List expectedAttrNameList = new ArrayList();
        expectedAttrNameList.add("uid"); //$NON-NLS-1$
        expectedAttrNameList.add("cn"); //$NON-NLS-1$
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
        		expectedCountLimit, expectedSearchScope, expectedSortKeys);
    }

	@Test
    public void testLT() throws Exception {
    	LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT UserID, Name FROM LdapModel.People WHERE Name < 'R'"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(!(cn>=R))"; //$NON-NLS-1$
        
        List expectedAttrNameList = new ArrayList();
        expectedAttrNameList.add("uid"); //$NON-NLS-1$
        expectedAttrNameList.add("cn"); //$NON-NLS-1$
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
        		expectedCountLimit, expectedSearchScope, expectedSortKeys);
    }
    
	private LDAPSearchDetails helpGetSearchDetails(String queryString) throws Exception {
    	QueryMetadataInterface metadata = exampleLdap();
    	Select query = (Select)getCommand(queryString, metadata);

    	LDAPExecutionFactory config = new LDAPExecutionFactory();
    	
    	IQueryToLdapSearchParser searchParser = new IQueryToLdapSearchParser(config);

        LDAPSearchDetails searchDetails = searchParser.translateSQLQueryToLDAPSearch(query);
		return searchDetails;
	}
    
	public static QueryMetadataInterface exampleLdap() throws Exception {
        String ddl = "create foreign table People (UserID string options (nameinsource 'uid'), Name string options (nameinsource 'cn'), dn string, vals string[]) options (nameinsource 'ou=people,dc=metamatrix,dc=com')"
                        + "create foreign table People_Groups (user_dn string options (nameinsource 'dn'), groupname string options (nameinsource 'memberOf', \"teiid_ldap:dn_prefix\" 'ou=groups,dc=metamatrix,dc=com', \"teiid_ldap:rdn_type\" 'cn')) options (nameinsource 'ou=people,dc=metamatrix,dc=com')";
        return RealMetadataFactory.fromDDL(ddl, "x", "LdapModel");
    }    
	
	@Test public void testJoin() throws Exception {
        LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT UserID, Name, people_groups.groupname FROM LdapModel.People inner join people_groups on people.dn = people_groups.user_dn where Name = 'R%'"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(cn=R%)"; //$NON-NLS-1$
        
        List<String> expectedAttrNameList = new ArrayList<String>();
        expectedAttrNameList.add("uid"); //$NON-NLS-1$
        expectedAttrNameList.add("cn"); //$NON-NLS-1$
        expectedAttrNameList.add("memberOf");
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
                        expectedCountLimit, expectedSearchScope, expectedSortKeys);
        
    }
        
	@Test public void testRdnTypePredicates() throws Exception {
        LDAPSearchDetails searchDetails = helpGetSearchDetails("SELECT people_groups.groupname FROM people_groups where groupname like 'R%' and groupname < 'q'"); //$NON-NLS-1$
        
        //-----------------------------------
        // Set Expected SearchDetails Values
        //-----------------------------------
        String expectedContextName = "ou=people,dc=metamatrix,dc=com"; //$NON-NLS-1$
        String expectedContextFilter = "(&(memberOf=cn=R*,ou=groups,dc=metamatrix,dc=com)(!(memberOf>=cn=q,ou=groups,dc=metamatrix,dc=com)))"; //$NON-NLS-1$
        
        List<String> expectedAttrNameList = new ArrayList<String>();
        expectedAttrNameList.add("memberOf");
        
        long expectedCountLimit = -1;
        int expectedSearchScope = SearchControls.ONELEVEL_SCOPE;
        SortKey[] expectedSortKeys = null;
        
        helpTestSearchDetails(searchDetails, expectedContextName, expectedContextFilter, expectedAttrNameList,
                        expectedCountLimit, expectedSearchScope, expectedSortKeys);
        
    }
}

