package eu.dnetlib.openaire.user.ldap;

import eu.dnetlib.openaire.user.IUserActions;
import eu.dnetlib.openaire.user.user.UserProfileIS;
import org.apache.log4j.Logger;

import java.util.UUID;

import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;


/**
 * Created by sofia on 31/10/2016.
 */
public class UserActionsLDAP implements IUserActions {

    transient Logger logger = Logger.getLogger(UserActionsLDAP.class);

    private int ldapPort = 0;
    private String ldapAddress;
    private String ldapUsername;
    private String ldapPassword;
    private String ldapUsersDN;

    @Override
    public boolean activateUser(String activationId) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("activating user with activationId " + activationId);
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("employeeNumber", activationId);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "uid");
            SearchResult searchResult = connection.search(searchRequest);
            String dn = null;

            if ( searchResult.getSearchEntries().size() > 0	) {

                for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                    dn = "uid=" + entry.getAttributeValue("uid") + "," + ldapUsersDN;
                }

                Modification mod1 = new Modification(ModificationType.REPLACE, "JoomlaBlockUser", "0");
                Modification mod2 = new Modification(ModificationType.REPLACE, "employeeNumber");
                connection.modify(dn, mod1, mod2);
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public String addUser(String email, String password) throws Exception {
        throw new UnsupportedOperationException();
    }

    @Override
    public String addUser(String username, String email, String password, String firstName, String lastName) throws Exception {
        logger.debug("adding user " + username + " " + email + " to ldap");
        Attribute cn = new Attribute("cn", username);
        Attribute displayName = new Attribute("displayName", firstName + " " + lastName);
        Attribute mail = new Attribute("mail", email);
        Attribute givenName = new Attribute("givenName", firstName);
        Attribute joomlaBlockUser = new Attribute("JoomlaBlockUser", "1");
        Attribute joomlaGroup = new Attribute("JoomlaGroup", "Registered");
        Attribute objectClass = new Attribute("objectClass", "top", "inetOrgPerson", "JoomlaUser");
        Attribute userPassword = new Attribute("userPassword", Joomla15PasswordHash.create(password));
        Attribute sn = new Attribute("sn", lastName);
        Attribute uid = new Attribute("uid", username);
        // Attribute joomlaUserParams = new Attribute("JoomlaUserParams", "");
        String activationId = UUID.randomUUID().toString();
        Attribute x500UniqueIdentifier = new Attribute("employeeNumber", activationId);
        LDAPConnection connection = null;
        try {
            DN dn = new DN("uid=" + username + "," + ldapUsersDN);
            System.out.println("cn: " + cn + " displayName: " + displayName + " mail: " + mail + " givenName: " + givenName + " joomlaBlockUser: " + joomlaBlockUser + " joomlaGroup: " + joomlaGroup + " objectClass: " + objectClass + " userPassword: " + userPassword + " sn: " + sn + " uid: " + uid + " x500UniqueIdentifier: " + x500UniqueIdentifier);
            Entry entry = new Entry(dn.toNormalizedString(), cn, displayName, mail, givenName, joomlaBlockUser, joomlaGroup, objectClass, userPassword, sn, uid/*
																																								 * ,
																																								 * joomlaUserParams
																																								 */, x500UniqueIdentifier);
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            connection.add(entry);

            return activationId;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public boolean correctCreds(String email, String password) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("checking if user " + email + " entered a correct password when logging in");
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("mail", email);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "userPassword");
            SearchResult searchResult = connection.search(searchRequest);
            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                if (Joomla15PasswordHash.check(password, entry.getAttributeValue("userPassword")))
                    return true;
            }
            return false;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public void editUser(String email, String fname, String lname, String inst) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("editing user " + email);
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("mail", email);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "uid");
            SearchResult searchResult = connection.search(searchRequest);
            String dn = null;
            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                dn = "uid=" + entry.getAttributeValue("uid") + "," + ldapUsersDN;
            }
            Modification mod1 = new Modification(ModificationType.REPLACE, "displayName", fname + " " + lname);
            Modification mod2 = new Modification(ModificationType.REPLACE, "givenName", fname);
            Modification mod3 = new Modification(ModificationType.REPLACE, "sn", lname);
            Modification mod4 = new Modification(ModificationType.REPLACE, "o", inst);
            connection.modify(dn, mod1, mod2, mod3, mod4);
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public eu.dnetlib.openaire.user.user.UserProfile getUser(String userIdentifier) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("getting user " + userIdentifier + " from ldap");
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("mail", userIdentifier);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "mail", "givenName", "sn", "o", "uid");
            SearchResult searchResult = connection.search(searchRequest);
            UserProfileIS profile = new UserProfileIS();
            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                profile.setEmail(entry.getAttributeValue("mail"));
                profile.setFname(entry.getAttributeValue("givenName"));
                profile.setLname(entry.getAttributeValue("sn"));
                profile.setInstitution(entry.getAttributeValue("o"));
                profile.setUsername(entry.getAttributeValue("uid"));
            }
            return profile;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public boolean isAdmin(String email) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("checking if user " + email + " is an administrator");
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("mail", email);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "JoomlaGroup");
            SearchResult searchResult = connection.search(searchRequest);

            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                for (String role : entry.getAttributeValues("JoomlaGroup"))
                    if (role.equals("validatorAdmin"))
                        return true;
            }
            logger.debug(email + " is not administrator");
            return false;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public boolean isUserActivated(String email) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("checking if user " + email + " is activated");
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("mail", email);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "JoomlaBlockUser");
            SearchResult searchResult = connection.search(searchRequest);
            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                int val = entry.getAttributeValueAsInteger("JoomlaBlockUser");
                if (val == 0)
                    return true;
                else
                    return false;
            }
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
        return false;
    }

    @Override
    public String prepareResetPassword(String email) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("preparing reset password for user " + email);
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("mail", email);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "uid");
            SearchResult searchResult = connection.search(searchRequest);
            String dn = null;
            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                dn = "uid=" + entry.getAttributeValue("uid") + "," + ldapUsersDN;
            }
            String uuid = UUID.randomUUID().toString();
            Modification mod = new Modification(ModificationType.REPLACE, "employeeNumber", uuid);
            connection.modify(dn, mod);
            return uuid;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public void resetPassword(String uuid, String password) throws Exception {
        LDAPConnection connection = null;
        try {
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("employeeNumber", uuid);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "uid");
            SearchResult searchResult = connection.search(searchRequest);
            String dn = null;
            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                dn = "uid=" + entry.getAttributeValue("uid") + "," + ldapUsersDN;
            }
            Modification mod1 = new Modification(ModificationType.REPLACE, "userPassword", Joomla15PasswordHash.create(password));
            Modification mod2 = new Modification(ModificationType.REPLACE, "employeeNumber");
            connection.modify(dn, mod1, mod2);
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public boolean userExists(String email) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("checking if user " + email + " exists in ldap");
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("mail", email);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "mail");

            SearchResult searchResult = connection.search(searchRequest);
            if (!searchResult.getSearchEntries().isEmpty())
                return true;

            return false;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public boolean usernameExists(String username) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("checking if user " + username + " exists in ldap");
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("uid", username);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "uid");
            SearchResult searchResult = connection.search(searchRequest);

            if (!searchResult.getSearchEntries().isEmpty()) {
                return true;
            }

            return false;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    @Override
    public String getEmailFromUsername(String username) throws Exception {
        LDAPConnection connection = null;
        try {
            logger.debug("getting email for user " + username);
            connection = new LDAPConnection(ldapAddress, ldapPort, ldapUsername, ldapPassword);
            Filter filter = Filter.createEqualityFilter("uid", username);
            SearchRequest searchRequest = new SearchRequest(ldapUsersDN, SearchScope.SUB, filter, "mail");
            SearchResult searchResult = connection.search(searchRequest);
            for (SearchResultEntry entry : searchResult.getSearchEntries()) {
                return entry.getAttributeValue("mail");
            }
            return null;
        } catch (Exception e) {
            logger.error("", e);
            throw e;
        } finally {
            if (connection != null)
                connection.close();
        }
    }

    public void setLdapPort(int ldapPort) {
        this.ldapPort = ldapPort;
    }

    public void setLdapAddress(String ldapAddress) {
        this.ldapAddress = ldapAddress;
    }

    public void setLdapUsername(String ldapUsername) {
        this.ldapUsername = ldapUsername;
    }

    public void setLdapPassword(String ldapPassword) {
        this.ldapPassword = ldapPassword;
    }

    public void setLdapUsersDN(String ldapUsersDN) {
        this.ldapUsersDN = ldapUsersDN;
    }


    public static void main(String[] args) {

        UserActionsLDAP ldap =  new UserActionsLDAP();

        String ldapAddress = "esperos.di.uoa.gr";
        String ldapUsername = "cn=admin,dc=openaire,dc=eu";
        String ldapPassword = "serenata";
        String ldapUsersDN = "ou=users,dc=openaire,dc=eu";
        int ldapPort = 389;

        ldap.setLdapAddress(ldapAddress);
        ldap.setLdapUsername(ldapUsername);
        ldap.setLdapPassword(ldapPassword);
        ldap.setLdapUsersDN(ldapUsersDN);
        ldap.setLdapPort(ldapPort);



        // CHECK: usernameExists(uid)
        // TERMINAL: ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu dn

//        try {
//            if (ldap.usernameExists("ngasparis"))
//                System.out.println("username exists");
//            else
//                System.out.println("username doesn't exist");
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }


        // CHECK: userExists(mail)
        // TERMINAL: ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu mail

//        try {
//            if (ldap.userExists("n.gasparis@di.uoa.gr"))
//                System.out.println("user exists");
//            else
//                System.out.println("user doesn't exist");
//        } catch (Exception e) {
//            e.printStackTrace();
//        }


        // CHECK: isUserActivated(mail)
        // TODO not sure!
        // TERMINAL: ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu mail

//        try {
//            if (ldap.isUserActivated("n.gasparis@di.uoa.gr"))
//                System.out.println("user is activated");
//            else
//                System.out.println("user isn't activated");
//        } catch (Exception e) {
//            e.printStackTrace();
//        }


        // CHECK: getUser(mail)
        // TERMINAL(mail): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu mail
        // TERMINAL(firstname): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu givenName
        // TERMINAL(lastname): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu sn
        // TERMINAL(institution): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu o
        // TERMINAL(username): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu uid

//        try {
//            gr.uoa.di.validator.api.user.UserProfileIS userProfile = (gr.uoa.di.validator.api.user.UserProfileIS) ldap.getUser("n.gasparis@di.uoa.gr");
//            System.out.println("User with Firstname: "        + userProfile.getFname() +
//                                  "\n          Lastname: "    + userProfile.getLname() +
//                                  "\n          Username: "    + userProfile.getUsername() +
//                                  "\n          Email: "       + userProfile.getEmail() +
//                                  "\n          Institution: " + userProfile.getInstitution());
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        // CHECK: getEmailFromUsername(username)
        // ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu mail

//        try {
//            if (ldap.usernameExists("user is adminngasparis")) {
//                String email = ldap.getEmailFromUsername("ngasparis");
//                System.out.println("username exists with email: " + email);
//            }
//            else
//                System.out.println("username doesn't exist");
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        // CHECK: isAdmin(mail)
        // TODO not sure

//        try {
//            if (ldap.isAdmin("n.gasparis@di.uoa.gr"))
//                System.out.println("user is admin");
//            else
//                System.out.println("user isn't admin");
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        // CHECK: addUser(username, email, password, firstname, lastname)
        // TERMINAL (check only): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu dn
//
//        try {
//            String activationId = ldap.addUser("sbaltzi", "sbaltzi@di.uoa.gr", "12345678", "sofia", "baltzi");
//            System.out.println("Added user with activation id:" + activationId);
//            gr.uoa.di.validator.api.user.UserProfileIS userProfile = (gr.uoa.di.validator.api.user.UserProfileIS) ldap.getUser("sbaltzi@di.uoa.gr");
//            System.out.println("User with Firstname: "        + userProfile.getFname() +
//                                  "\n          Lastname: "    + userProfile.getLname() +
//                                  "\n          Username: "    + userProfile.getUsername() +
//                                  "\n          Email: "       + userProfile.getEmail() +
//                                  "\n          Institution: " + userProfile.getInstitution());
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        // CHECK: correctCreds(mail, password)

//        try {
//            if (ldap.correctCreds("sbaltzi@di.uoa.gr", "12345678"))
//                System.out.println("correct credentials");
//            else
//                System.out.println("wrong credentials");
//        } catch (Exception e) {
//            e.printStackTrace();
//        }


        // CHECK: editUser()
        // TERMINAL(mail): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu mail
        // TERMINAL(firstname): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu givenName
        // TERMINAL(lastname): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu sn
        // TERMINAL(institution): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu o
        // TERMINAL(username): ldapsearch -x -LLL -h esperos.di.uoa.gr -b dc=openaire,dc=eu uid

//        try {
//            ldap.editUser("sbaltzi@di.uoa.gr", "sofie", "baltzi", "di");
//            gr.uoa.di.validator.api.user.UserProfileIS userProfile = (gr.uoa.di.validator.api.user.UserProfileIS) ldap.getUser("sbaltzi@di.uoa.gr");
//            System.out.println("User with Firstname: "        + userProfile.getFname() +
//                                  "\n          Lastname: "    + userProfile.getLname() +
//                                  "\n          Username: "    + userProfile.getUsername() +
//                                  "\n          Email: "       + userProfile.getEmail() +
//                                  "\n          Institution: " + userProfile.getInstitution());
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        // CHECK: prepareResetPassword(mail)
        // CHECK: resetPassword(mail, newpassword)

//        try {
//            String uuid = ldap.prepareResetPassword("sbaltzi@di.uoa.gr");
//            System.out.println("uuid is " + uuid);
//            ldap.resetPassword(uuid, "12345678");
//            if (ldap.correctCreds("sbaltzi@di.uoa.gr", "12345678"))
//                System.out.println("correct credentials");
//            else
//                System.out.println("wrong credentials");
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

    }
}
