package org.example.Utils;

import javax.naming.*;
import javax.naming.directory.*;
import java.util.Hashtable;

public class ADUtil {
    public static Boolean ADLogin = null;
    /**
     * LDAP绑定路径  LDAP://test.com
     */
    public static String ADPath;
    /**
     * 管理员登录帐号
     */
    public static String ADUser;
    /**
     * 管理员登录密码
     */
    public static String ADPwd;

    /**
     * 根据用户公共名称取得用户对象
     *
     * @param commonName 用户公共名称
     * @return 如果找到该用户，则返回用户对象；否则返回 null
     */
    public static DirContext getDirectoryEntry(String commonName) {
        DirContext ctx = getDirectoryObject();
        if (ctx == null) return null;

        try {
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String filter = "(&(&(objectCategory=person)(objectClass=user))(cn=" + commonName + "))";

            NamingEnumeration<SearchResult> results = ctx.search("", filter, controls);
            if (results.hasMore()) {
                SearchResult result = results.next();
                // 通过 getObject() 获取上下文对象并强制转换
                Object obj = result.getObject();
                if (obj instanceof DirContext) {
                    return (DirContext) obj;
                }
            }
            return null;
        } catch (NamingException e) {
            return null;
        } finally {
            closeContext(ctx);
        }
    }

    /**
     * 根据用户公共名称和密码取得用户对象
     *
     * @param commonName 用户公共名称
     * @param password   用户密码
     * @return 如果找到该用户，则返回用户对象；否则返回 null
     */
    public static DirContext getDirectoryEntry(String commonName, String password) {
        DirContext ctx = getDirectoryObject(commonName, password);
        if (ctx == null) return null;

        try {
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String filter = "(&(&(objectCategory=person)(objectClass=user))(cn=" + commonName + "))";

            NamingEnumeration<SearchResult> results = ctx.search("", filter, controls);
            if (results.hasMore()) {
                SearchResult result = results.next();
                // 通过 getObject() 获取上下文对象并强制转换
                Object obj = result.getObject();
                if (obj instanceof DirContext) {
                    return (DirContext) obj;
                }
            }
            return null;
        } catch (NamingException e) {
            return null;
        } finally {
            closeContext(ctx);
        }
    }

    /**
     * 根据用户帐号称取得用户对象
     *
     * @param sAMAccountName 用户帐号名
     * @return 如果找到该用户，则返回用户对象；否则返回 null
     */
    public static DirContext getDirectoryEntryByAccount(String sAMAccountName) {
        DirContext ctx = getDirectoryObject();
        if (ctx == null) return null;

        try {
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String filter = "(&(objectCategory=user)(sAMAccountName=" + sAMAccountName + "))";

            NamingEnumeration<SearchResult> results = ctx.search("", filter, controls);
            if (results.hasMore()) {
                SearchResult result = results.next();
                return getDirectoryObject(result.getNameInNamespace());
            }
            return null;
        } catch (NamingException e) {
            return null;
        } finally {
            closeContext(ctx);
        }
    }

    /**
     * 根据用户帐号和密码取得用户对象
     *
     * @param sAMAccountName 用户帐号名
     * @param password       用户密码
     * @return 如果找到该用户，则返回回用户对象；否则返回 null
     */
    public static DirContext getDirectoryEntryByAccount(String sAMAccountName, String password) {
        DirContext de = getDirectoryEntryByAccount(sAMAccountName);
        if (de != null) {
            try {
                Attributes attrs = de.getAttributes("");
                Attribute cnAttr = attrs.get("cn");
                if (cnAttr != null) {
                    String commonName = (String) cnAttr.get();
                    if (getDirectoryEntry(commonName, password) != null) {
                        return getDirectoryEntry(commonName, password);
                    }
                }
            } catch (NamingException e) {
                return null;
            } finally {
                closeContext(de);
            }
        }
        return null;
    }

    /**
     * 根据组名取得用户组对象
     *
     * @param groupName 组名
     * @return 如果找到该组，则返回组对象；否则返回 null
     */
    public static DirContext getDirectoryEntryOfGroup(String groupName) {
        DirContext ctx = getDirectoryObject();
        if (ctx == null) return null;

        try {
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String filter = "(&(objectClass=group)(cn=" + groupName + "))";

            NamingEnumeration<SearchResult> results = ctx.search("", filter, controls);
            if (results.hasMore()) {
                SearchResult result = results.next();
                // 通过 getObject() 获取上下文对象并强制转换
                Object obj = result.getObject();
                if (obj instanceof DirContext) {
                    return (DirContext) obj;
                }
            }
            return null;
        } catch (NamingException e) {
            return null;
        } finally {
            closeContext(ctx);
        }
    }

    /**
     * 创建目录服务上下文对象
     */
    private static DirContext getDirectoryObject() {
        return getDirContext(ADPath, ADUser, ADPwd, "simple");
    }

    private static DirContext getDirectoryObject(String userName, String password) {
        return getDirContext(ADPath, userName, password, "simple");
    }

    private static DirContext getDirectoryObject(String domainReference) {
        return getDirContext(ADPath + domainReference, ADUser, ADPwd, "simple");
    }

    private static DirContext getDirectoryObject(String domainReference, String userName, String password) {
        return getDirContext(ADPath + domainReference, userName, password, "simple");
    }

    /**
     * 创建LDAP连接上下文
     */
    private static DirContext getDirContext(String url, String user, String password, String authType) {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, url);
        env.put(Context.SECURITY_AUTHENTICATION, authType);

        if (user != null && !user.isEmpty()) {
            env.put(Context.SECURITY_PRINCIPAL, user);
        }
        if (password != null && !password.isEmpty()) {
            env.put(Context.SECURITY_CREDENTIALS, password);
        }

        try {
            return new InitialDirContext(env);
        } catch (NamingException e) {
            return null;
        }
    }

    /**
     * 关闭上下文连接
     */
    private static void closeContext(DirContext ctx) {
        if (ctx != null) {
            try {
                ctx.close();
            } catch (NamingException e) {
                // 忽略关闭异常
            }
        }
    }

    /**
     * 判断用户与密码是否足够以满足身份验证进而登录
     *
     * @param commonName 用户公共名称
     * @param password   密码
     * @return 登录结果枚举
     */
    public static LoginResult login(String commonName, String password) {
        DirContext de = getDirectoryEntry(commonName, password);
        if (de != null) {
            try {
                // 获取用户账号控制属性
                Attribute uacAttr = de.getAttributes("").get("userAccountControl");
                if (uacAttr != null) {
                    int userAccountControl = Integer.parseInt(uacAttr.get().toString());
                    if (!isAccountActive(userAccountControl)) {
                        return LoginResult.LOGIN_USER_ACCOUNT_INACTIVE;
                    }
                }

                if (getDirectoryEntry(commonName, password) != null) {
                    return LoginResult.LOGIN_USER_OK;
                } else {
                    return LoginResult.LOGIN_USER_PASSWORD_INCORRECT;
                }
            } catch (NamingException e) {
                return LoginResult.LOGIN_USER_PASSWORD_INCORRECT;
            } finally {
                closeContext(de);
            }
        } else {
            return LoginResult.LOGIN_USER_DOESNT_EXIST;
        }
    }

    /**
     * 判断用户帐号与密码是否足够以满足身份验证进而登录
     *
     * @param sAMAccountName 用户帐号
     * @param password       密码
     * @return 登录结果枚举
     */
    public static LoginResult loginByAccount(String sAMAccountName, String password) {
        DirContext de = getDirectoryEntryByAccount(sAMAccountName);
        if (de != null) {
            try {
                // 获取用户账号控制属性
                Attribute uacAttr = de.getAttributes("").get("userAccountControl");
                if (uacAttr != null) {
                    int userAccountControl = Integer.parseInt(uacAttr.get().toString());
                    if (!isAccountActive(userAccountControl)) {
                        return LoginResult.LOGIN_USER_ACCOUNT_INACTIVE;
                    }
                }

                if (getDirectoryEntryByAccount(sAMAccountName, password) != null) {
                    return LoginResult.LOGIN_USER_OK;
                } else {
                    return LoginResult.LOGIN_USER_PASSWORD_INCORRECT;
                }
            } catch (NamingException e) {
                return LoginResult.LOGIN_USER_PASSWORD_INCORRECT;
            } finally {
                closeContext(de);
            }
        } else {
            return LoginResult.LOGIN_USER_DOESNT_EXIST;
        }
    }

    /**
     * 判断用户帐号是否激活
     *
     * @param userAccountControl 用户帐号属性控制器
     * @return 如果用户帐号已经激活，返回 true；否则则返回 false
     */
    public static boolean isAccountActive(int userAccountControl) {
        int userAccountControlDisabled = ADS_USER_FLAG_ENUM.ADS_UF_ACCOUNTDISABLE.getValue();
        int flagExists = userAccountControl & userAccountControlDisabled;
        return flagExists <= 0;
    }

    /**
     * 用户登录验证结果
     */
    public enum LoginResult {
        /**
         * 正常登录
         */
        LOGIN_USER_OK(1),
        /**
         * 用户不存在
         */
        LOGIN_USER_DOESNT_EXIST(2),
        /**
         * 用户帐号被禁用
         */
        LOGIN_USER_ACCOUNT_INACTIVE(3),
        /**
         * 用户密码不正确
         */
        LOGIN_USER_PASSWORD_INCORRECT(4);

        private final int value;

        LoginResult(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

    /**
     * AD用户标志枚举
     */
    public enum ADS_USER_FLAG_ENUM {
        /**
         * 登录脚本标志
         */
        ADS_UF_SCRIPT(0X0001),
        /**
         * 用户帐号禁用标志
         */
        ADS_UF_ACCOUNTDISABLE(0X0002),
        /**
         * 主文件夹标志
         */
        ADS_UF_HOMEDIR_REQUIRED(0X0008),
        /**
         * 过期标志
         */
        ADS_UF_LOCKOUT(0X0010),
        /**
         * 用户密码不是必须的
         */
        ADS_UF_PASSWD_NOTREQD(0X0020),
        /**
         * 密码不能更改标志
         */
        ADS_UF_PASSWD_CANT_CHANGE(0X0040),
        /**
         * 使用可逆的加密保存密码
         */
        ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED(0X0080),
        /**
         * 本地帐号标志
         */
        ADS_UF_TEMP_DUPLICATE_ACCOUNT(0X0100),
        /**
         * 普通用户的默认帐号类型
         */
        ADS_UF_NORMAL_ACCOUNT(0X0200),
        /**
         * 跨域的信任帐号标志
         */
        ADS_UF_INTERDOMAIN_TRUST_ACCOUNT(0X0800),
        /**
         * 工作站信任帐号标志
         */
        ADS_UF_WORKSTATION_TRUST_ACCOUNT(0x1000),
        /**
         * 服务器信任帐号标志
         */
        ADS_UF_SERVER_TRUST_ACCOUNT(0X2000),
        /**
         * 密码永不过期标志
         */
        ADS_UF_DONT_EXPIRE_PASSWD(0X10000),
        /**
         * MNS 帐号标志
         */
        ADS_UF_MNS_LOGON_ACCOUNT(0X20000),
        /**
         * 交互式登录必须使用智能卡
         */
        ADS_UF_SMARTCARD_REQUIRED(0X40000),
        /**
         * 服务帐号将通过 Kerberos 委托信任
         */
        ADS_UF_TRUSTED_FOR_DELEGATION(0X80000),
        /**
         * 敏感帐号不能被委托
         */
        ADS_UF_NOT_DELEGATED(0X100000),
        /**
         * 此帐号需要 DES 加密类型
         */
        ADS_UF_USE_DES_KEY_ONLY(0X200000),
        /**
         * 不要进行 Kerberos 预身份验证
         */
        ADS_UF_DONT_REQUIRE_PREAUTH(0X4000000),
        /**
         * 用户密码过期标志
         */
        ADS_UF_PASSWORD_EXPIRED(0X800000),
        /**
         * 用户帐号号可委托标志
         */
        ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION(0X1000000);

        private final int value;

        ADS_USER_FLAG_ENUM(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }
}