package org.slc.sli.api.ldap;

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

import javax.naming.directory.SearchControls;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.stereotype.Component;

/**
 * implementation of LdapService interface for basic CRUD and search operations on LDAP directory
 *
 * @author dliu
 *
 */

@Component
public class LdapServiceImpl implements LdapService {

    @Autowired
    LdapTemplate ldapTemplate;

    @Value("${sli.simple-idp.userSearchAttribute}")
    private String userSearchAttribute;

    @Value("${sli.simple-idp.userObjectClass}")
    private String userObjectClass;

    @Value("${sli.simple-idp.groupSearchAttribute}")
    private String groupSearchAttribute;

    @Value("${sli.simple-idp.groupObjectClass}")
    private String groupObjectClass;

    public LdapServiceImpl() {
    }

    @Override
    public User getUser(String realm, String uid) {
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", userObjectClass)).and(new EqualsFilter(userSearchAttribute, uid));
        DistinguishedName dn = new DistinguishedName("ou=" + realm);
        User user;
        try {
            List userList = ldapTemplate.search(dn, filter.toString(), SearchControls.SUBTREE_SCOPE, new String[] { "*", CREATE_TIMESTAMP, MODIFY_TIMESTAMP }, new UserContextMapper());
            if (userList == null || userList.size() == 0) {
                throw new EmptyResultDataAccessException(1);
            } else if (userList.size() > 1) {
                throw new IncorrectResultSizeDataAccessException("User must be unique", 1);
            }
            user = (User) userList.get(0);
            user.setUid(uid);
            List<Group> groups = getUserGroups(realm, uid);
            if (groups != null && groups.size() > 0) {
                for (Group group : groups) {
                    user.addGroup(group.getGroupName());
                }
            }
        } catch (EmptyResultDataAccessException e) {
            return null;
        }
        return user;
    }

    @Override
    public List<Group> getUserGroups(String realm, String uid) {
        DistinguishedName dn = new DistinguishedName("ou=" + realm);
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", groupObjectClass)).and(new EqualsFilter(groupSearchAttribute, uid));
        @SuppressWarnings("unchecked")
        List<Group> groups = ldapTemplate.search(dn, filter.toString(), new GroupContextMapper());
        return groups;
    }

    @Override
    public void removeUser(String realm, String uid) {
        // TODO Auto-generated method stub

    }

    @Override
    public String createUser(String realm, User user) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String updateUser(String realm, User user) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<User> findUserByGroups(String realm, List<String> groupNames) {
        List<User> users = new ArrayList<User>();
        List<String> uids = new ArrayList<String>();
        for (String groupName : groupNames) {
            Group group = getGroup(realm, groupName);
            List<String> memberUids = group.getMemberUids();
            if (memberUids != null && memberUids.size() > 0) {
                for (String memberUid : memberUids) {
                    if (!uids.contains(memberUid)) {
                        uids.add(memberUid);
                        User user = getUser(realm, memberUid);
                        if (user != null) {
                            users.add(user);
                        }
                    }
                }
            }
        }
        return users;
    }

    @Override
    public List<User> findUserByGroups(String realm, List<String> groupNames, String tenant) {
        return filterByTenant(findUserByGroups(realm, groupNames), tenant);
    }

    @Override
    public List<User> findUserByAttributes(String realm, List<String> attributes) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Group getGroup(String realm, String groupName) {
        DistinguishedName dn = new DistinguishedName("ou=" + realm);
        AndFilter filter = new AndFilter();
        filter.and(new EqualsFilter("objectclass", groupObjectClass)).and(new EqualsFilter("cn", groupName));
        Group group = (Group) ldapTemplate.searchForObject(dn, filter.toString(), new GroupContextMapper());
        return group;
    }

    private List<User> filterByTenant(List<User> users, String tenant) {
        if (tenant == null) {
            return users;
        }

        List<User> filteredUsers = new ArrayList<User>();
        for (User user : users) {
            if (tenant.equals(user.getTenant())) {
                filteredUsers.add(user);
            }
        }
        return filteredUsers;
    }
}

@@ -581,32 +581,81 @@
 r.EqualsFilter;%0A
+import org.springframework.ldap.filter.OrFilter;%0A
 import org.sprin
@@ -3428,32 +3428,67 @@
 rn null;%0A    %7D%0A%0A
+    @SuppressWarnings(%22unchecked%22)%0A
     @Override%0A  
@@ -4068,205 +4068,819 @@
     
-    User user = getUser(realm, memberUid);%0A                        if (user != null) %7B%0A                            users.add(user);%0A                        %7D%0A                    %7D%0A                %7D
+%7D%0A                %7D%0A            %7D%0A        %7D%0A        AndFilter filter = new AndFilter();%0A        filter.and(new EqualsFilter(%22objectclass%22, userObjectClass));%0A        OrFilter orFilter = new OrFilter();%0A        for(String uid:uids)%7B%0A            orFilter.or(new EqualsFilter(userSearchAttribute, uid));%0A        %7D%0A        filter.and(orFilter);%0A        DistinguishedName dn = new DistinguishedName(%22ou=%22 + realm);%0A        users = (List%3CUser%3E) (ldapTemplate.search(dn, filter.toString(), SearchControls.SUBTREE_SCOPE, new String%5B%5D %7B%0A                %22*%22, CREATE_TIMESTAMP, MODIFY_TIMESTAMP %7D, new UserContextMapper()));%0A        for (User user : users) %7B%0A            List%3CGroup%3E groups = getUserGroups(realm, user.getUid());%0A            for (Group group : groups) %7B%0A                user.addGroup(group.getGroupName());
 %0A   

update finduserbygroups in ldapserviceimpl