package com.yifu.cloud.plus.v1.yifu.common.ldap.util;

import com.unboundid.ldap.sdk.*;
import com.unboundid.ldap.sdk.controls.SubentriesRequestControl;
import com.yifu.cloud.plus.v1.yifu.common.ldap.config.LdapProperties;
import com.yifu.cloud.plus.v1.yifu.common.ldap.entity.PersonVo;
import com.yifu.cloud.plus.v1.yifu.common.ldap.mapper.PersonAttributesMapper;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.ldap.core.LdapTemplate;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import static org.springframework.ldap.query.LdapQueryBuilder.query;

@Log4j2
@EnableConfigurationProperties(LdapProperties.class)
public class LdapUtil {

	@Autowired
	private LdapTemplate ldapTemplate;

	@Autowired
	private LdapProperties ldapProperties;

	private static LdapContext ctx = null;

	private static final Control[] connCtls = null;

	/**
	 * @return List<SearchResultEntry>
	 * @author huyc
	 * @date 2022-06-14 17:56
	 * @description get请求获取指定对象信息
	 **/
	public List<SearchResultEntry> getAllPersonNamesWithTraditionalWay() {
		List<SearchResultEntry> result = new ArrayList<>();
		try (LDAPConnection connection = new LDAPConnection(ldapProperties.getUrl(), ldapProperties.getPort(),
				ldapProperties.getUserName(), ldapProperties.getPassword())) {
			SearchRequest searchRequest = new SearchRequest(ldapProperties.getBaseDn(), SearchScope.SUB,
					"(objectclass=*)");
			searchRequest.addControl(new SubentriesRequestControl());
			SearchResult searchResult = connection.search(searchRequest);
			result.addAll(searchResult.getSearchEntries());
		} catch (LDAPException e) {
			e.printStackTrace();
		}
		return result;
	}

	public List<PersonVo> getAllPersons() {
		return ldapTemplate.search(query()
				.where("objectclass").is("posixAccount"), new PersonAttributesMapper());
	}

	// 校验用户名密码的方法
	public Boolean authenticate(String usr, String pwd) {
		boolean valide;
		if (pwd == null || "".equals(pwd))
			return false;
		if (ctx == null) {
			getCtx();
		}
		String userDN = getUserDN(usr);
		if ("".equals(userDN) || userDN == null) {
			return false;
		}
		try {
			ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN);
			ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, pwd);
			ctx.reconnect(connCtls);
			valide = true;
			closeCtx();
		} catch (NamingException e) {
			log.error(userDN + " is not authenticated");
			valide = false;
		}
		return valide;
	}

	public void getCtx() {
		if (ctx != null) {
			return;
		}
		Hashtable<String, String> env = new Hashtable<>();
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		env.put(Context.PROVIDER_URL, "ldap://" + ldapProperties.getUrl() + ":" + ldapProperties.getPort() +
				"/" + ldapProperties.getDn());
		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		env.put(Context.SECURITY_PRINCIPAL, ldapProperties.getUserName());
		env.put(Context.SECURITY_CREDENTIALS, ldapProperties.getPassword());
		try {
			// 链接ldap
			ctx = new InitialLdapContext(env, connCtls);
		} catch (NamingException e) {
			e.printStackTrace();
		}
	}

	public void closeCtx() throws NamingException {
		if (ctx != null)
			ctx.close();
	}

	public String getUserDN(String uid) {
		StringBuilder userDN = new StringBuilder();
		try {
			SearchControls constraints = new SearchControls();
			constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
			NamingEnumeration<?> en = ctx.search("", "uid=" + uid, constraints);
			while (en != null && en.hasMoreElements()) {
				Object obj = en.nextElement();
				if (obj instanceof javax.naming.directory.SearchResult) {
					javax.naming.directory.SearchResult si = (javax.naming.directory.SearchResult) obj;
					userDN.append(si.getName());
					userDN.append(",").append(ldapProperties.getDn());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return userDN.toString();
	}
}
