package com.hxhq.common.ad;

import com.hxhq.common.core.web.domain.AjaxResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.Map;

/**
 * 纯JDK实现的AD域控核心接口（Spring Bean化）
 * 核心功能：1. 账号密码鉴权 2. 验证账号是否存在（基于只读账号）
 */
@Component
public class JdkADAuthUtil {
    private static final Logger log = LoggerFactory.getLogger(JdkADAuthUtil.class);
    private final ADProperties adProperties;
    private final String readOnlyUsername;
    private final String readOnlyPassword;

    @Value("${spring.profiles.active:dev}")
    private String activeProfile;

    // ====================== dev 环境多测试账号配置 ======================
    private static final Map<String, String> DEV_TEST_ACCOUNTS = new HashMap<>();

    static {

        DEV_TEST_ACCOUNTS.put("hxhq", "hxhq123");
    }

    /**
     * 构造器注入AD配置
     */
    public JdkADAuthUtil(ADProperties adProperties) {
        this.adProperties = adProperties;
        this.readOnlyUsername = adProperties.getReadOnly().getUsername();
        this.readOnlyPassword = adProperties.getReadOnly().getPassword();
    }

    // ====================== 核心接口1：AD账号密码鉴权 ======================
    public AjaxResult validateAccount(String username, String password) {
        if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()) {
            log.debug("用户名或密码为空，鉴权失败");
            return AjaxResult.error("用户名或密码为空，鉴权失败");
        }

        // ====== dev 多账号校验 ======
        if ("dev".equals(activeProfile)) {
            String trimUser = username.trim();
            if (DEV_TEST_ACCOUNTS.containsKey(trimUser) && DEV_TEST_ACCOUNTS.get(trimUser).equals(password.trim())) {
                log.debug("DEV环境测试账号鉴权成功：{}", username);
                return AjaxResult.success("账号[" + username + "]鉴权成功");
            }
        }

        LdapContext ldapContext = null;
        try {
            Hashtable<String, String> env = buildLDAPEnv(username, password);
            log.debug("开始鉴权，用户主体名：{}", env.get(Context.SECURITY_PRINCIPAL));

            ldapContext = new InitialLdapContext(env, null);
            log.debug("账号[{}]鉴权成功", username);
            return AjaxResult.success("账号[" + username + "]鉴权成功");

        } catch (NamingException e) {
            String errorMsg = e.getMessage().toLowerCase();
            log.error("账号[{}]鉴权失败：{}", username, errorMsg);

            if (errorMsg.contains("data 525")) {
                return AjaxResult.error("鉴权失败原因：用户不存在");
            } else if (errorMsg.contains("data 52e")) {
                return AjaxResult.error("鉴权失败原因：密码错误");
            } else if (errorMsg.contains("data 532")) {
                return AjaxResult.error("鉴权失败原因：密码过期");
            } else if (errorMsg.contains("data 533")) {
                return AjaxResult.error("鉴权失败原因：账号禁用");
            } else if (errorMsg.contains("data 775")) {
                return AjaxResult.error("鉴权失败原因：账号锁定");
            }
            return AjaxResult.error("账号[" + username + "]鉴权失败");
        } finally {
            closeLdapContext(ldapContext);
        }
    }

    // ====================== 核心接口2：验证AD账号是否存在 ======================
    public AjaxResult checkAccountExists(String username) {
        if (username == null || username.trim().isEmpty()) {
            return AjaxResult.error("用户名不能为空，查询失败");
        }

        String trimUser = username.trim();

        // ====== dev 多账号存在校验 ======
        if ("dev".equals(activeProfile)) {
            // 支持纯账号 或 hxhq@xxx.com
            String pureUser = trimUser.contains("@") ? trimUser.split("@")[0] : trimUser;
            if (DEV_TEST_ACCOUNTS.containsKey(pureUser)) {
                log.debug("DEV环境测试账号存在：{}", username);
                return AjaxResult.success("账号[" + username + "]在AD域中存在");
            }
        }

        LdapContext ldapContext = null;
        NamingEnumeration<SearchResult> results = null;
        try {
            Hashtable<String, String> env = buildLDAPEnvForQuery();
            ldapContext = new InitialLdapContext(env, null);

            SearchControls searchControls = new SearchControls();
            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            searchControls.setReturningAttributes(new String[]{"sAMAccountName", "userPrincipalName"});
            searchControls.setTimeLimit(3000);

            String filter = trimUser.contains("@")
                    ? String.format("(userPrincipalName=%s)", escapeLDAPFilter(trimUser))
                    : String.format("(sAMAccountName=%s)", escapeLDAPFilter(trimUser));

            results = ldapContext.search(adProperties.getBaseDn(), filter, searchControls);
            boolean exists = results != null && results.hasMoreElements();

            if (exists) {
                return AjaxResult.success("账号[" + username + "]在AD域中存在");
            } else {
                return AjaxResult.error("账号[" + username + "]在AD域中不存在");
            }
        } catch (IllegalArgumentException e) {
            log.error("查询失败：{}", e.getMessage());
            return AjaxResult.error("查询失败：" + e.getMessage());
        } catch (NamingException e) {
            log.error("查询账号[{}]是否存在失败：{}", username, e.getMessage());
            if (e.getMessage().toLowerCase().contains("error code 49")) {
                return AjaxResult.error("查询失败原因：只读账号绑定失败");
            }
            return AjaxResult.error("查询账号[" + username + "]是否存在失败");
        } finally {
            closeNamingEnumeration(results);
            closeLdapContext(ldapContext);
        }
    }

    // ====================== 以下是原有工具方法，完全不变 ======================
    private Hashtable<String, String> buildLDAPEnv(String username, String password) {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

        String ldapUrl = adProperties.isUseSsl()
                ? "ldaps://" + adProperties.getServerHost() + ":" + adProperties.getServerPort()
                : "ldap://" + adProperties.getServerHost() + ":" + adProperties.getServerPort();
        env.put(Context.PROVIDER_URL, ldapUrl);
        env.put(Context.SECURITY_AUTHENTICATION, "simple");

        String userPrincipal = username;
        if (!username.contains("@")) {
            String domainSuffix = adProperties.getBaseDn().replaceAll("DC=", "").replace(",", ".");
            userPrincipal = username + "@" + domainSuffix;
        }
        env.put(Context.SECURITY_PRINCIPAL, userPrincipal);
        env.put(Context.SECURITY_CREDENTIALS, password);
        env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(adProperties.getConnectTimeout()));
        return env;
    }

    private Hashtable<String, String> buildLDAPEnvForQuery() {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

        String ldapUrl = adProperties.isUseSsl()
                ? "ldaps://" + adProperties.getServerHost() + ":" + adProperties.getServerPort()
                : "ldap://" + adProperties.getServerHost() + ":" + adProperties.getServerPort();
        env.put(Context.PROVIDER_URL, ldapUrl);
        env.put(Context.SECURITY_AUTHENTICATION, "simple");

        if (readOnlyUsername == null || readOnlyUsername.trim().isEmpty()
                || readOnlyPassword == null || readOnlyPassword.trim().isEmpty()) {
            throw new IllegalArgumentException("只读账号未配置，无法执行AD账号查询！");
        }

        String domainSuffix = adProperties.getBaseDn().replaceAll("DC=", "").replace(",", ".");
        String readOnlyUpn = readOnlyUsername + "@" + domainSuffix;
        env.put(Context.SECURITY_PRINCIPAL, readOnlyUpn);
        env.put(Context.SECURITY_CREDENTIALS, readOnlyPassword);
        env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(adProperties.getConnectTimeout()));
        return env;
    }

    private String escapeLDAPFilter(String input) {
        if (input == null || input.isEmpty()) return input;
        return input.replace("\\", "\\5c")
                .replace("(", "\\28")
                .replace(")", "\\29")
                .replace("*", "\\2a")
                .replace("\0", "\\00");
    }

    private void closeLdapContext(LdapContext ctx) {
        if (ctx != null) {
            try { ctx.close(); } catch (NamingException e) { log.warn("关闭LDAP连接失败"); }
        }
    }

    private void closeNamingEnumeration(NamingEnumeration<?> ne) {
        if (ne != null) {
            try { ne.close(); } catch (NamingException e) { log.warn("关闭LDAP结果集失败"); }
        }
    }

    public static void main(String[] args) {
//
//        // 1. 配置AD基础信息（替换为你的实际配置）
//        ADProperties adConfig = new ADProperties();
//        adConfig.setServerHost("172.21.10.1");
//        adConfig.setServerPort(389);
//        adConfig.setBaseDn("DC=glpcd,DC=com");
//        adConfig.setUseSsl(false);
//        adConfig.setConnectTimeout(5000);
//        adConfig.getReadOnly().setUsername("adcon");
//        adConfig.getReadOnly().setPassword("JYL@it323");
//
//        // 2. 初始化工具类（★核心★：传入只读账号，替换为你的实际只读账号/密码）
//        JdkADAuthUtil adAuthUtil = new JdkADAuthUtil(adConfig);
//
//        // 3. 测试账号
//        String existUsername = "ELNtest01";       // 存在的账号
//        String nonExistUsername = "test_not_exists"; // 不存在的账号
//        String testPassword = "GLPcd_28";        // 测试账号密码
//
//        // 4. 测试接口2：验证账号是否存在（无需用户密码）
//        AjaxResult exists = adAuthUtil.checkAccountExists(existUsername);
//        System.out.println("===== 验证账号[" + existUsername + "]是否存在：" + (exists.isSuccess() ? "存在" : "不存在") + " =====");
//
//        AjaxResult exists1 = adAuthUtil.checkAccountExists("ELNtest01@glpcd.com");
//        System.out.println("===== 验证账号[" + existUsername + "]是否存在：" + (exists1.isSuccess()? "存在" : "不存在") + " =====");
//
//        AjaxResult nonExists = adAuthUtil.checkAccountExists(nonExistUsername);
//        System.out.println("===== 验证账号[" + nonExistUsername + "]是否存在：" + (nonExists.isSuccess() ? "存在" : "不存在") + " =====");
//
//        // 5. 测试接口1：账号密码鉴权
//        AjaxResult authSuccess = adAuthUtil.validateAccount(existUsername, testPassword);
//        System.out.println("===== 账号[" + existUsername + "]鉴权结果：" + (authSuccess.isSuccess() ? "成功" : "失败") + " =====");
//
//        AjaxResult authFail = adAuthUtil.validateAccount(existUsername, "wrong_password");
//        System.out.println("===== 账号[" + existUsername + "]错误密码鉴权结果：" + (authFail.isSuccess() ? "成功" : "失败") + " =====");
    }
}